├── .gitignore ├── .github └── workflows │ ├── manual-oracle.com-all.yml │ ├── jdk.java.net-uri-list.yml │ ├── jdk.java.net-uri-update.yml │ ├── test.yml │ ├── manual-java.net-ea-latest.yml │ ├── manual-oracle.com-17-latest.yml │ ├── manual-all-environments.yml │ ├── manual-oracle.com-17-archive.yml │ ├── manual-uri.yml │ ├── manual-website-release-version.yml │ ├── manual-java.net-all.yml │ └── print-java-homes.yml ├── LICENSE.txt ├── action.yml ├── test ├── Validate.java └── Test.java ├── CHANGELOG.md ├── src ├── ListOpenJavaDevelopmentKits.java └── Download.java ├── README.md └── jdk.java.net-uri.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | classes/ 3 | 4 | *.iml 5 | -------------------------------------------------------------------------------- /.github/workflows/manual-oracle.com-all.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | jobs: 5 | latest-17: 6 | uses: ./.github/workflows/manual-oracle.com-17-latest.yml 7 | archive-17: 8 | uses: ./.github/workflows/manual-oracle.com-17-archive.yml 9 | -------------------------------------------------------------------------------- /.github/workflows/jdk.java.net-uri-list.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | name: 5 | description: 'List JDK uris at https://jdk.java.net/{NAME}/' 6 | required: false 7 | type: string 8 | 9 | jobs: 10 | list: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 14 | - run: $JAVA_HOME_21_X64/bin/java --show-version src/ListOpenJavaDevelopmentKits.java ${{ github.event.inputs.name }} 15 | -------------------------------------------------------------------------------- /.github/workflows/jdk.java.net-uri-update.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: '23 */2 * * *' 4 | workflow_dispatch: 5 | 6 | jobs: 7 | update-jdk-uri-properties: 8 | runs-on: ubuntu-latest 9 | permissions: write-all 10 | steps: 11 | - run: curl --output /dev/null --verbose --head --fail https://jdk.java.net 12 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 13 | - run: $JAVA_HOME_21_X64/bin/java src/ListOpenJavaDevelopmentKits.java > jdk.java.net-uri.properties 14 | - run: | 15 | git diff 16 | git config user.name github_actions_dev 17 | git config user.email github_actions_dev_grp@oracle.com 18 | git add . 19 | if [ -z "$(git status --porcelain)" ]; then 20 | echo 'No changes detected.' 21 | exit 0 22 | fi 23 | git commit --message 'Update `jdk.java.net-uri.properties`' 24 | git push 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ main, 'v*' ] 4 | pull_request: 5 | branches: [ '*' ] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | test: 10 | name: "Test Download" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: 'Check out repository' 14 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 15 | - name: 'Compile and run test' 16 | shell: bash 17 | run: | 18 | PATH=$JAVA_HOME_21_X64/bin:$PATH 19 | javac -d classes src/Download.java test/Test.java 20 | java -cp classes Test 21 | validate: 22 | name: "Validate Values" 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: 'Check out repository' 26 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 27 | - name: 'Run validation program' 28 | shell: bash 29 | run: | 30 | PATH=$JAVA_HOME_21_X64/bin:$PATH 31 | java test/Validate.java 32 | -------------------------------------------------------------------------------- /.github/workflows/manual-java.net-ea-latest.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | jobs: 5 | latest: 6 | name: "Latest JDK ${{ matrix.release }} on ${{ matrix.os }}" 7 | strategy: 8 | fail-fast: false 9 | max-parallel: 3 10 | matrix: 11 | os: [ ubuntu-latest , macos-latest, windows-latest ] 12 | release: [ 'ea' ] 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: 'Check out repository' 16 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 17 | - name: 'Set up JDK' 18 | id: setup 19 | uses: ./ 20 | with: 21 | website: jdk.java.net 22 | release: ${{ matrix.release }} 23 | version: latest 24 | - name: 'Print outputs' 25 | shell: bash 26 | run: | 27 | echo 'Outputs' 28 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 29 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 30 | - name: 'Print Java version' 31 | run: java --version 32 | -------------------------------------------------------------------------------- /.github/workflows/manual-oracle.com-17-latest.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | latest: 7 | name: "Latest JDK ${{ matrix.release }} on ${{ matrix.os }}" 8 | strategy: 9 | fail-fast: false 10 | max-parallel: 3 11 | matrix: 12 | os: [ ubuntu-latest , macos-latest, windows-latest ] 13 | release: [ 17 ] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - name: 'Check out repository' 17 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 18 | - name: 'Set up JDK' 19 | id: setup 20 | uses: ./ 21 | with: 22 | website: oracle.com 23 | release: ${{ matrix.release }} 24 | version: latest 25 | - name: 'Print outputs' 26 | shell: bash 27 | run: | 28 | echo 'Outputs' 29 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 30 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 31 | - name: 'Print Java version' 32 | run: java --version 33 | -------------------------------------------------------------------------------- /.github/workflows/manual-all-environments.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | jobs: 5 | test: 6 | name: "Test ${{ matrix.os }} with default JDK" 7 | strategy: 8 | fail-fast: false 9 | max-parallel: 3 10 | matrix: 11 | # https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job#choosing-github-hosted-runners 12 | os: [ ubuntu-latest, ubuntu-24.04, macos-latest, macos-15, macos-14, macos-13, windows-latest, windows-2025, windows-2022, windows-2019 ] 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: 'Check out repository' 16 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 17 | - name: 'Set up default JDK' 18 | id: setup 19 | uses: ./ 20 | - name: 'Print outputs' 21 | shell: bash 22 | run: | 23 | echo 'Outputs' 24 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 25 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 26 | - name: 'Print Java version' 27 | run: java -version 28 | -------------------------------------------------------------------------------- /.github/workflows/manual-oracle.com-17-archive.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | archive: 7 | name: "Archived JDK ${{ matrix.version }} on ${{ matrix.os }}" 8 | strategy: 9 | fail-fast: false 10 | max-parallel: 3 11 | matrix: 12 | os: [ ubuntu-latest , macos-latest, windows-latest ] 13 | release: [ 17 ] 14 | version: [ 17, 17.0.1, 17.0.2, 17.0.3, 17.0.3.1, 17.0.4, 17.0.4.1, 17.0.5, 17.0.6, 17.0.7, 17.0.8 ] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - name: 'Check out repository' 18 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 19 | - name: 'Set up JDK' 20 | id: setup 21 | uses: ./ 22 | with: 23 | website: oracle.com 24 | release: ${{ matrix.release }} 25 | version: ${{ matrix.version }} 26 | - name: 'Print outputs' 27 | shell: bash 28 | run: | 29 | echo 'Outputs' 30 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 31 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 32 | - name: 'Print Java version' 33 | run: java --version 34 | -------------------------------------------------------------------------------- /.github/workflows/manual-uri.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | os: 5 | description: 'The type of machine to run the job on' 6 | default: 'ubuntu-latest' 7 | required: true 8 | type: choice 9 | options: 10 | - 'ubuntu-latest' 11 | - 'macos-latest' 12 | - 'macos-14' 13 | - 'windows-latest' 14 | uri: 15 | description: 'URI of JDK archive file to download' 16 | required: true 17 | type: string 18 | install: 19 | description: 'Run actions/setup-java to install the downloaded JDK' 20 | default: 'true' 21 | required: true 22 | type: boolean 23 | 24 | jobs: 25 | test: 26 | name: "Setup JDK from URI on ${{ github.event.inputs.os }}" 27 | runs-on: ${{ github.event.inputs.os }} 28 | steps: 29 | - name: 'Check out repository' 30 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 31 | - name: 'Set up JDK' 32 | id: setup 33 | uses: ./ 34 | with: 35 | uri: ${{ github.event.inputs.uri }} 36 | install: ${{ github.event.inputs.install }} 37 | - name: 'Print outputs' 38 | shell: bash 39 | run: | 40 | echo 'Outputs' 41 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 42 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 43 | - name: 'Print Java version' 44 | run: java -version 45 | -------------------------------------------------------------------------------- /.github/workflows/manual-website-release-version.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | website: 5 | description: 'Site to download JDK from' 6 | default: 'oracle.com' 7 | required: true 8 | type: choice 9 | options: 10 | - 'oracle.com' 11 | - 'jdk.java.net' 12 | release: 13 | description: 'Release number or project name' 14 | default: '21' 15 | required: true 16 | type: string 17 | version: 18 | description: 'Version' 19 | default: 'latest' 20 | required: true 21 | type: string 22 | install: 23 | description: 'Run actions/setup-java to install the downloaded JDK' 24 | default: true 25 | required: true 26 | type: boolean 27 | 28 | jobs: 29 | test: 30 | name: "JDK ${{ github.event.inputs.release }} (${{ github.event.inputs.version }}) on ${{ matrix.os }}" 31 | strategy: 32 | fail-fast: false 33 | max-parallel: 3 34 | matrix: 35 | os: [ ubuntu-latest, macos-latest, macos-14, windows-latest ] 36 | runs-on: ${{ matrix.os }} 37 | steps: 38 | - name: 'Check out repository' 39 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 40 | - name: 'Set up JDK' 41 | id: setup 42 | uses: ./ 43 | with: 44 | website: ${{ github.event.inputs.website }} 45 | release: ${{ github.event.inputs.release }} 46 | version: ${{ github.event.inputs.version }} 47 | install: ${{ github.event.inputs.install }} 48 | - name: 'Print outputs' 49 | shell: bash 50 | run: | 51 | echo 'Outputs' 52 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 53 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 54 | - name: 'Print Java version' 55 | run: java -version 56 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Oracle and/or its affiliates. 2 | 3 | The Universal Permissive License (UPL), Version 1.0 4 | 5 | Subject to the condition set forth below, permission is hereby granted to any 6 | person obtaining a copy of this software, associated documentation and/or data 7 | (collectively the "Software"), free of charge and under any and all copyright 8 | rights in the Software, and any and all patent rights owned or freely 9 | licensable by each licensor hereunder covering either (i) the unmodified 10 | Software as contributed to or provided by such licensor, or (ii) the Larger 11 | Works (as defined below), to deal in both 12 | 13 | (a) the Software, and 14 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 15 | one is included with the Software (each a "Larger Work" to which the Software 16 | is contributed by such licensors), 17 | 18 | without restriction, including without limitation the rights to copy, create 19 | derivative works of, display, perform, and distribute the Software and make, 20 | use, sell, offer for sale, import, export, have made, and have sold the 21 | Software and the Larger Work(s), and to sublicense the foregoing rights on 22 | either these or other terms. 23 | 24 | This license is subject to the following condition: 25 | The above copyright notice and either this complete permission notice or at 26 | a minimum a reference to the UPL must be included in all copies or 27 | substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | SOFTWARE. 36 | -------------------------------------------------------------------------------- /.github/workflows/manual-java.net-all.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | jobs: 5 | releases: 6 | name: "JDK ${{ matrix.release }} on ${{ matrix.os }}" 7 | strategy: 8 | fail-fast: false 9 | max-parallel: 3 10 | matrix: 11 | os: [ ubuntu-latest, macos-latest, windows-latest ] 12 | release: [ 22, 21, 20 ] 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: 'Check out repository' 16 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 17 | - name: 'Set up JDK' 18 | id: setup 19 | uses: ./ 20 | with: 21 | website: jdk.java.net 22 | release: ${{ matrix.release }} 23 | - name: 'Print outputs' 24 | shell: bash 25 | run: | 26 | echo 'Outputs' 27 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 28 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 29 | - name: 'Print Java version' 30 | run: java -version 31 | 32 | projects: 33 | name: "Project ${{ matrix.project }} on ${{ matrix.os }}" 34 | needs: [ releases ] 35 | strategy: 36 | fail-fast: false 37 | max-parallel: 3 38 | matrix: 39 | os: [ ubuntu-latest, macos-latest, windows-latest ] 40 | project: [ jextract, loom, panama, valhalla ] 41 | exclude: 42 | - { os: macos-latest, project: valhalla } # missing 'libjli.dylib' 43 | runs-on: ${{ matrix.os }} 44 | steps: 45 | - name: 'Check out repository' 46 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 47 | - name: 'Set up JDK' 48 | id: setup 49 | uses: ./ 50 | with: 51 | website: jdk.java.net 52 | release: ${{ matrix.project }} 53 | - name: 'Print outputs' 54 | shell: bash 55 | run: | 56 | echo 'Outputs' 57 | echo "steps.setup.outputs.archive = ${{ steps.setup.outputs.archive }}" 58 | echo "steps.setup.outputs.version = ${{ steps.setup.outputs.version }}" 59 | - name: 'Print Java version' 60 | run: java -version 61 | -------------------------------------------------------------------------------- /.github/workflows/print-java-homes.yml: -------------------------------------------------------------------------------- 1 | name: Print Java Versions 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | print: 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ubuntu-22.04, ubuntu-20.04, macos-14, macos-13, macos-12, macos-11, windows-2022, windows-2019] 12 | name: "${{ matrix.os }} x ${{ matrix.jdk }}" 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: 'JAVA_HOME_8_*' 16 | shell: bash 17 | run: | 18 | echo "JAVA_HOME_8_X64 = $JAVA_HOME_8_X64" 19 | if [ -d "$JAVA_HOME_8_X64" ]; then 20 | $JAVA_HOME_8_X64/bin/java -version 21 | fi 22 | echo "JAVA_HOME_8_arm64 = $JAVA_HOME_8_arm64" 23 | if [ -d "$JAVA_HOME_8_arm64" ]; then 24 | $JAVA_HOME_8_arm64/bin/java -version 25 | fi 26 | - name: 'JAVA_HOME_11_*' 27 | shell: bash 28 | run: | 29 | echo "JAVA_HOME_11_X64 = $JAVA_HOME_11_X64" 30 | if [ -d "$JAVA_HOME_11_X64" ]; then 31 | $JAVA_HOME_11_X64/bin/java -version 32 | fi 33 | echo "JAVA_HOME_11_arm64 = $JAVA_HOME_11_arm64" 34 | if [ -d "$JAVA_HOME_11_arm64" ]; then 35 | $JAVA_HOME_11_arm64/bin/java -version 36 | fi 37 | - name: 'JAVA_HOME_17_*' 38 | shell: bash 39 | run: | 40 | echo "JAVA_HOME_17_X64 = $JAVA_HOME_17_X64" 41 | if [ -d "$JAVA_HOME_17_X64" ]; then 42 | $JAVA_HOME_17_X64/bin/java -version 43 | fi 44 | echo "JAVA_HOME_17_arm64 = $JAVA_HOME_17_arm64" 45 | if [ -d "$JAVA_HOME_17_arm64" ]; then 46 | $JAVA_HOME_17_arm64/bin/java -version 47 | fi 48 | - name: 'JAVA_HOME_21_*' 49 | shell: bash 50 | run: | 51 | echo "JAVA_HOME_21_X64 = $JAVA_HOME_21_X64" 52 | if [ -d "$JAVA_HOME_21_X64" ]; then 53 | $JAVA_HOME_21_X64/bin/java -version 54 | fi 55 | echo "JAVA_HOME_21_arm64 = $JAVA_HOME_21_arm64" 56 | if [ -d "$JAVA_HOME_21_arm64" ]; then 57 | $JAVA_HOME_21_arm64/bin/java -version 58 | fi 59 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Java Development Kits built by Oracle' 2 | description: 'Download and set up a JDK built by Oracle' 3 | 4 | branding: 5 | icon: 'download-cloud' 6 | color: 'red' 7 | 8 | inputs: 9 | website: 10 | description: 'Site to download JDK from: `oracle.com` or `jdk.java.net`' 11 | required: true 12 | default: 'oracle.com' 13 | release: 14 | description: 'Feature release number or project name, defaults to `25`' 15 | required: true 16 | default: '25' 17 | version: 18 | description: 'Additional version information, defaults to `latest`' 19 | required: true 20 | default: 'latest' 21 | install: 22 | description: 'Install the downloaded JDK archive file by running actions/setup-java, defaults to `true`' 23 | required: true 24 | default: 'true' 25 | install-as-version: 26 | description: 27 | Controls which value is passed as `java-version` to actions/setup-java, defaults to `PARSE_URI` 28 | if `release` starts with a digit, else it defaults to `HASH_URI` 29 | required: false 30 | uri: 31 | description: 'URI of JDK archive file to download' 32 | required: false 33 | 34 | outputs: 35 | archive: 36 | description: 'The path to the downloaded JDK archive file' 37 | value: ${{ steps.download.outputs.archive }} 38 | version: 39 | description: 'The version of the downloaded JDK' 40 | value: ${{ steps.download.outputs.version }} 41 | 42 | runs: 43 | using: 'composite' 44 | steps: 45 | - name: 'Download Java Development Kit' 46 | id: download 47 | shell: bash 48 | env: 49 | WEBSITE: "${{ inputs.website }}" 50 | RELEASE: "${{ inputs.release }}" 51 | VERSION: "${{ inputs.version }}" 52 | INSTALL_AS_VERSION: "${{ inputs.install-as-version }}" 53 | URI: "${{ inputs.uri }}" 54 | run: | 55 | echo "::group::Prepare download..." 56 | JAVA=$JAVA_HOME_21_X64/bin/java 57 | if [ ! -d "$JAVA_HOME_21_X64" ]; then 58 | JAVA=$JAVA_HOME_21_arm64/bin/java 59 | fi 60 | $JAVA --version 61 | DOWNLOAD=$GITHUB_ACTION_PATH/src/Download.java 62 | echo "::endgroup::" 63 | if [ ! -z "$URI" ]; then 64 | $JAVA \ 65 | -Dinstall-as-version="$INSTALL_AS_VERSION" \ 66 | "$DOWNLOAD" \ 67 | "$URI" 68 | else 69 | $JAVA \ 70 | -Dinstall-as-version="$INSTALL_AS_VERSION" \ 71 | "$DOWNLOAD" \ 72 | "$WEBSITE" \ 73 | "$RELEASE" \ 74 | "$VERSION" 75 | fi 76 | - name: 'Install Java Development Kit' 77 | if: ${{ inputs.install == 'true' }} 78 | uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 79 | with: 80 | java-version: ${{ steps.download.outputs.version }} 81 | distribution: jdkfile 82 | jdkFile: ${{ steps.download.outputs.archive }} 83 | -------------------------------------------------------------------------------- /test/Validate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2024, Oracle and/or its affiliates. 3 | * 4 | * This source code is licensed under the UPL license found in the 5 | * LICENSE.txt file in the root directory of this source tree. 6 | */ 7 | 8 | import java.net.URI; 9 | import java.net.http.HttpClient; 10 | import java.net.http.HttpClient.Redirect; 11 | import java.net.http.HttpRequest; 12 | import java.net.http.HttpRequest.BodyPublishers; 13 | import java.net.http.HttpResponse.BodyHandlers; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.util.List; 17 | import java.util.Properties; 18 | import java.util.TreeSet; 19 | import java.util.stream.Stream; 20 | 21 | class Validate { 22 | 23 | public static void main(String... args) throws Exception { 24 | var validate = new Validate(); 25 | var paths = args.length == 0 ? findProperties() : Stream.of(args).map(Path::of).toList(); 26 | for (var path : paths) validate.validateProperties(path); 27 | } 28 | 29 | static List findProperties() throws Exception { 30 | var syntaxAndPattern = "glob:*.properties"; 31 | System.out.println(syntaxAndPattern); 32 | var directory = Path.of(""); 33 | var matcher = directory.getFileSystem().getPathMatcher(syntaxAndPattern); 34 | try (var stream = Files.find(directory, 9, (path, attributes) -> matcher.matches(path))) { 35 | return stream.toList(); 36 | } 37 | } 38 | 39 | final HttpClient http = HttpClient.newBuilder().followRedirects(Redirect.NORMAL).build(); 40 | 41 | void validateProperties(Path path) throws Exception { 42 | if (Files.notExists(path)) throw new IllegalArgumentException("no such file: " + path); 43 | System.out.println(); 44 | System.out.println(path); 45 | var properties = new Properties(); 46 | properties.load(Files.newBufferedReader(path)); 47 | for (var key : new TreeSet<>(properties.stringPropertyNames())) { 48 | var value = properties.getProperty(key); 49 | validateProperty(key, value); 50 | } 51 | } 52 | 53 | private void validateProperty(String key, String value) throws Exception { 54 | if (value.startsWith("http")) { 55 | var uri = URI.create(value); 56 | var request = HttpRequest.newBuilder(uri).method("HEAD", BodyPublishers.noBody()).build(); 57 | var response = http.send(request, BodyHandlers.discarding()); 58 | if (response.statusCode() == 200) { 59 | var size = response.headers().firstValueAsLong("Content-Length").orElse(-1); 60 | System.out.printf("%s%n", key); 61 | System.out.printf(" uri = %s%n", uri); 62 | System.out.printf(" size = %,11d bytes%n", size); 63 | } else { 64 | response 65 | .headers() 66 | .map() 67 | .forEach((header, entry) -> System.err.printf("%s -> %s%n", header, entry)); 68 | throw new AssertionError("Status for '%s' not OK: %s".formatted(key, response)); 69 | } 70 | return; 71 | } 72 | System.err.printf("Unknown property protocol %s=%s%n", key, value); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 4 | 5 | This project uses tags and branches for [release management](https://docs.github.com/en/actions/creating-actions/about-custom-actions#using-tags-for-release-management). 6 | 7 | 8 | ## [Unreleased] 9 | _nothing noteworthy yet_ 10 | 11 | ## [1.5.0] - 2025-09-19 12 | ### Changed 13 | - Default value of `release` input to Java `25` 14 | - Use SHA-pinned versions of other actions 15 | 16 | ## [1.4.2] - 2025-05-23 17 | - Various improvements 18 | 19 | ## [1.4.1] - 2025-03-19 20 | ### Changed 21 | - Default value of `release` input to Java `24` 22 | 23 | ## [1.4.0] - 2024-03-21 24 | ### Added 25 | - Java `24` and project `leyden` to the list of Early-Access releases 26 | - New `version: stable` mode for `release: ea` setups 27 | - Retry of failed JDK archive downloads 28 | ### Changed 29 | - Default value of `release` input to Java `23` 30 | 31 | ## [1.3.4] - 2024-03-21 32 | ### Fixed 33 | - Support running on ARM64 machines 34 | ### Changed 35 | - Default value of `release` input to Java `22` 36 | - Run action with pre-installed Java 21 37 | 38 | ## [1.3.3] - 2024-01-29 39 | ### Added 40 | - Java `23` to the list of Early-Access releases 41 | ### Changed 42 | - Update version of `setup-java` to `v4` 43 | 44 | ## [1.3.2] - 2023-09-19 45 | ### Added 46 | - Java `22` to the list of Early-Access releases 47 | ### Changed 48 | - Default value of `release` input to Java `21` 49 | 50 | ## [1.3.1] - 2022-10-20 51 | ### Added 52 | - Project `genzgc` to the list of Early-Access releases 53 | ### Fixed 54 | - Use GitHub's environment file to set output values 55 | 56 | ## [1.3.0] - 2022-09-21 57 | ### Changed 58 | - Default value of `release` input to Java `19` 59 | 60 | ## [1.2.1] - 2022-08-08 61 | ### Fixed 62 | - Default `install-as-version` value computation 63 | 64 | ## [1.2.0] - 2022-08-08 65 | ### Added 66 | - Project `jextract` to the list of Early-Access releases 67 | - New `install-as-version` input 68 | 69 | ## [1.1.2] - 2022-06-10 70 | ### Added 71 | - Java `20` to the list of Early-Access releases 72 | 73 | ## [1.1.1] - 2022-05-03 74 | ### Fixed 75 | - Version string parsing when update segment is present 76 | 77 | ## [1.1.0] - 2022-04-01 78 | ### Changed 79 | - Version of `setup-java` to `v3` 80 | - Default value of `release` input to Java `18` 81 | 82 | ## [1.0.0] - 2022-02-18 83 | ### Added 84 | - Initial Release 85 | 86 | [Unreleased]: https://github.com/oracle-actions/setup-java/compare/v1.5.0...HEAD 87 | [1.5.0]: https://github.com/oracle-actions/setup-java/compare/v1.4.2...v1.5.0 88 | [1.4.2]: https://github.com/oracle-actions/setup-java/compare/v1.4.1...v1.4.2 89 | [1.4.1]: https://github.com/oracle-actions/setup-java/compare/v1.4.0...v1.4.1 90 | [1.4.0]: https://github.com/oracle-actions/setup-java/compare/v1.3.4...v1.4.0 91 | [1.3.4]: https://github.com/oracle-actions/setup-java/compare/v1.3.3...v1.3.4 92 | [1.3.3]: https://github.com/oracle-actions/setup-java/compare/v1.3.2...v1.3.3 93 | [1.3.2]: https://github.com/oracle-actions/setup-java/compare/v1.3.1...v1.3.2 94 | [1.3.1]: https://github.com/oracle-actions/setup-java/compare/v1.3.0...v1.3.1 95 | [1.3.0]: https://github.com/oracle-actions/setup-java/compare/v1.2.1...v1.3.0 96 | [1.2.1]: https://github.com/oracle-actions/setup-java/compare/v1.2.0...v1.2.1 97 | [1.2.0]: https://github.com/oracle-actions/setup-java/compare/v1.1.2...v1.2.0 98 | [1.1.2]: https://github.com/oracle-actions/setup-java/compare/v1.1.1...v1.1.2 99 | [1.1.1]: https://github.com/oracle-actions/setup-java/compare/v1.1.0...v1.1.1 100 | [1.1.0]: https://github.com/oracle-actions/setup-java/compare/v1.0.0...v1.1.0 101 | [1.0.0]: https://github.com/oracle-actions/setup-java/releases/tag/v1.0.0 102 | -------------------------------------------------------------------------------- /test/Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2024, Oracle and/or its affiliates. 3 | * 4 | * This source code is licensed under the UPL license found in the 5 | * LICENSE.txt file in the root directory of this source tree. 6 | */ 7 | 8 | import java.util.ArrayList; 9 | import java.util.stream.Stream; 10 | 11 | public class Test { 12 | 13 | static Download.Browser BROWSER = new Download.Browser(); 14 | static ArrayList ERRORS = new ArrayList<>(); 15 | 16 | public static void main(String[] args) { 17 | checkAllOracleJDKs(); 18 | checkAllJavaNetJDKs(); 19 | checkUnsupportedInputs(); 20 | 21 | if (ERRORS.isEmpty()) return; 22 | 23 | System.err.println(); 24 | System.err.printf("// %d error%s detected%n", ERRORS.size(), ERRORS.size() == 1 ? "" : "s"); 25 | ERRORS.forEach(System.err::println); 26 | System.exit(1); 27 | } 28 | 29 | static void checkAllOracleJDKs() { 30 | System.out.println(); 31 | System.out.println("// oracle.com - latest"); 32 | checkOracleJDK("25", "latest"); 33 | checkOracleJDK("21", "latest"); 34 | 35 | System.out.println(); 36 | System.out.println("// oracle.com - archive"); 37 | Stream.of("25", "25.0.1").forEach(version -> checkOracleJDK("25", version)); 38 | Stream.of("24", "24.0.1").forEach(version -> checkOracleJDK("24", version)); 39 | Stream.of("23", "23.0.1").forEach(version -> checkOracleJDK("23", version)); 40 | Stream.of("22", "22.0.1", "22.0.2").forEach(version -> checkOracleJDK("22", version)); 41 | Stream.of("21", "21.0.1", "21.0.2", "21.0.4").forEach(version -> checkOracleJDK("21", version)); 42 | Stream.of("20", "20.0.1", "20.0.2").forEach(version -> checkOracleJDK("20", version)); 43 | Stream.of("19", "19.0.1", "19.0.2").forEach(version -> checkOracleJDK("19", version)); 44 | Stream.of("18", "18.0.1", "18.0.1.1").forEach(version -> checkOracleJDK("18", version)); 45 | Stream.of("17", "17.0.1", "17.0.2", "17.0.12").forEach(version -> checkOracleJDK("17", version)); 46 | } 47 | 48 | static void checkAllJavaNetJDKs() { 49 | System.out.println(); 50 | System.out.println("// jdk.java.net - GA - latest"); 51 | checkJavaNetJDK("ga", "latest"); 52 | 53 | System.out.println(); 54 | System.out.println("// jdk.java.net - EA - latest"); 55 | checkJavaNetJDK("ea", "latest"); 56 | System.out.println("// jdk.java.net - EA - stable"); 57 | checkJavaNetJDK("ea", "stable"); 58 | 59 | System.out.println(); 60 | System.out.println("// jdk.java.net - Project Valhalla - latest"); 61 | checkJavaNetJDK("valhalla", "latest"); 62 | } 63 | 64 | static void checkOracleJDK(String release, String version) { 65 | checkJDK("oracle.com", new Download.JDK(release, version, "linux", "aarch64", "tar.gz")); 66 | checkJDK("oracle.com", new Download.JDK(release, version, "linux", "x64", "tar.gz")); 67 | checkJDK("oracle.com", new Download.JDK(release, version, "macos", "aarch64", "tar.gz")); 68 | checkJDK("oracle.com", new Download.JDK(release, version, "macos", "x64", "tar.gz")); 69 | checkJDK("oracle.com", new Download.JDK(release, version, "windows", "x64", "zip")); 70 | } 71 | 72 | static void checkJavaNetJDK(String release, String version) { 73 | checkJDK("jdk.java.net", new Download.JDK(release, version, "linux", "x64", "tar.gz")); 74 | checkJDK("jdk.java.net", new Download.JDK(release, version, "macos", "x64", "tar.gz")); 75 | checkJDK("jdk.java.net", new Download.JDK(release, version, "windows", "x64", "zip")); 76 | } 77 | 78 | static void checkJDK(String website, Download.JDK jdk) { 79 | System.out.println(website + " << " + jdk); 80 | var finder = Download.Website.find(website).orElseThrow(); 81 | var uri = finder.findUri(jdk).orElseThrow(); 82 | try { 83 | var head = BROWSER.head(uri); 84 | if (head.statusCode() < 200 || head.statusCode() >= 400) ERRORS.add(head.toString()); 85 | System.out.println(head); 86 | var sha = BROWSER.head(uri + ".sha256"); 87 | System.out.println(sha); 88 | if (sha.statusCode() >= 200 && sha.statusCode() < 400) { 89 | System.out.println(" --> " + BROWSER.browse(uri + ".sha256")); 90 | } else { 91 | System.out.println(" --> "); 92 | } 93 | } catch (Exception exception) { 94 | ERRORS.add(jdk + "\n" + exception); 95 | } 96 | } 97 | 98 | static void checkUnsupportedInputs() { 99 | System.out.println(); 100 | System.out.println("// Check unsupported inputs"); 101 | assertThrows(() -> Download.main(true), "Usage:"); 102 | assertThrows(() -> Download.main(true, "website"), "Could not find website for website"); 103 | assertThrows(() -> Download.main(true, "oracle.com", "0"), "Could not find URI of JDK"); 104 | } 105 | 106 | static void assertThrows(Runnable runnable, String snippet) { 107 | try { 108 | runnable.run(); 109 | } catch (Throwable throwable) { 110 | var message = throwable.toString(); 111 | if (message.contains(snippet)) return; 112 | var format = "Caught %s, but expected snippet '%s' not found in message: %s"; 113 | throw new AssertionError(String.format(format, throwable.getClass(), snippet, message)); 114 | } 115 | throw new AssertionError("Caught nothing?"); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/ListOpenJavaDevelopmentKits.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2025, Oracle and/or its affiliates. 3 | * 4 | * This source code is licensed under the UPL license found in the 5 | * LICENSE.txt file in the root directory of this source tree. 6 | */ 7 | 8 | import java.net.URI; 9 | import java.net.http.HttpClient; 10 | import java.net.http.HttpRequest; 11 | import java.net.http.HttpResponse; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.TreeMap; 15 | import java.util.regex.Pattern; 16 | import java.util.stream.Stream; 17 | 18 | /** 19 | * List Java Development Kit builds hosted at: {@code https://jdk.java.net}. 20 | * 21 | *

Example output: 22 | * 23 | *

{@code
 24 |  * 17,17.0.2,linux,x64=https://[...]/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz
 25 |  * 17,latest,linux,x64=https://[...]/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz
 26 |  * ga,latest,linux,x64=https://[...]/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz
 27 |  * }
28 | * 29 | * Keys are composed of {@code RELEASE,VERSION,OS-NAME,OS-ARCH} with: 30 | * 31 | * 37 | */ 38 | class ListOpenJavaDevelopmentKits { 39 | /** List of pages to visit and parse for JDK archives. */ 40 | static final List PAGES = 41 | List.of( 42 | // JDK: General-Availability Release 43 | Page.of("25") // https://jdk.java.net/25 44 | .withAlias("25,latest") 45 | .withAlias("ga,latest"), 46 | // JDK: Early-Access Releases 47 | Page.of("26") // https://jdk.java.net/26 48 | .withAlias("26,latest") 49 | .withAlias("ea,stable"), 50 | Page.of("27") // https://jdk.java.net/27 51 | .withAlias("27,latest") 52 | .withAlias("ea,latest"), 53 | // Named projects, usually in EA phase 54 | Page.of("jextract") // https://jdk.java.net/jextract 55 | .withAlias("jextract,latest") 56 | .withAlias("jextract,ea"), 57 | Page.of("loom") // https://jdk.java.net/loom 58 | .withAlias("loom,latest") 59 | .withAlias("loom,ea"), 60 | Page.of("leyden") // https://jdk.java.net/leyden 61 | .withAlias("leyden,latest") 62 | .withAlias("leyden,ea"), 63 | Page.of("valhalla") // https://jdk.java.net/valhalla 64 | .withAlias("valhalla,latest") 65 | .withAlias("valhalla,ea")); 66 | 67 | /** Regex-based pattern used to find a download URI in an HTML code line. */ 68 | static final Pattern URI_PATTERN = 69 | Pattern.compile( 70 | ".+?href=\"" 71 | + "(\\Qhttps://download.java.net/\\E.+?/openjdk-.+?_bin\\.(tar\\.gz|zip))" 72 | + "\".+?"); 73 | 74 | /** Regex-based pattern used to find all named groups of a download key. */ 75 | static final Pattern KEY_PATTERN = 76 | Pattern.compile(".+?openjdk-(?.+?)_(?.+?)-(?.+?)_bin\\.(?.+?)"); 77 | 78 | public static void main(String... args) { 79 | var pages = args.length == 1 ? List.of(Page.of(args[0])) : PAGES; 80 | var parser = new Parser(); 81 | for (var page : pages) { 82 | var section = parser.parse(page); 83 | System.out.println("#"); 84 | System.out.println("# " + page.address()); 85 | System.out.println("#"); 86 | section.map().forEach((key, uri) -> System.out.printf("%s=%s%n", key, uri)); 87 | } 88 | } 89 | 90 | record Page(String name, List aliases) { 91 | static Page of(String name) { 92 | return new Page(name, List.of()); 93 | } 94 | 95 | Page withAlias(String alias) { 96 | 97 | return new Page(name, Stream.concat(aliases.stream(), Stream.of(alias)).toList()); 98 | } 99 | 100 | String address() { 101 | return "https://jdk.java.net/" + name.toLowerCase() + "/"; 102 | } 103 | } 104 | 105 | record Section(Page page, Map map) {} 106 | 107 | record Parser(HttpClient http) { 108 | Parser() { 109 | this(HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build()); 110 | } 111 | 112 | String browse(String uri) { 113 | try { 114 | var request = HttpRequest.newBuilder(URI.create(uri)).build(); 115 | return http.send(request, HttpResponse.BodyHandlers.ofString()).body(); 116 | } catch (Exception exception) { 117 | return exception.toString(); 118 | } 119 | } 120 | 121 | Section parse(Page page) { 122 | var map = new TreeMap(); 123 | var html = browse(page.address()); 124 | for (var line : html.lines().toList()) { 125 | var uriMatcher = URI_PATTERN.matcher(line); 126 | if (!uriMatcher.matches()) { 127 | continue; 128 | } 129 | var uri = uriMatcher.group(1); 130 | var keyMatcher = KEY_PATTERN.matcher(uri); 131 | if (!keyMatcher.matches()) { 132 | System.err.println("// no match -> " + uri); 133 | continue; 134 | } 135 | var version = Runtime.Version.parse(keyMatcher.group("version")); 136 | var os = keyMatcher.group("os"); 137 | var arch = keyMatcher.group("arch"); 138 | var platform = os.equals("osx") ? "macos" : os + "," + arch; 139 | map.put(version.feature() + "," + version + "," + platform, uri); 140 | for (var alias : page.aliases()) { 141 | map.put(alias + "," + platform, uri); 142 | } 143 | } 144 | return new Section(page, map); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oracle-actions/setup-java 2 | 3 | This action downloads a Java Development Kit (JDK) built by Oracle and installs it using [`actions/setup-java`](https://github.com/actions/setup-java). 4 | 5 | JDKs built by Oracle are [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) and [Oracle OpenJDK](https://jdk.java.net). 6 | 7 | ## Input Overview 8 | 9 | | Input Name | Default Value | Description | 10 | |-----------------------|--------------:|-----------------------------------------------------------------| 11 | | `website` | `oracle.com` | From where the JDK should be downloaded. | 12 | | `release` | `25` | Java feature release number or name of an Early-Access project. | 13 | | `version` | `latest` | An explicit version of a Java release. | 14 | | `install` | `true` | Install the downloaded JDK archive file. | 15 | | `install-as-version` | _empty_ | Control the value passed as `java-version` | 16 | | `uri` | _empty_ | Custom URI of a JDK archive file to download. | 17 | 18 | ### Input `website` 19 | 20 | The `website` input specifies from where the JDK should be downloaded. 21 | It defaults to `oracle.com`. 22 | 23 | The following values are supported: 24 | 25 | - [`oracle.com`](https://www.oracle.com/java/technologies/downloads/) for Oracle JDK 21 and later. 26 | 27 | This action only supports Oracle JDKs provided under the [Oracle No-Fee Terms and Conditions License](https://www.java.com/freeuselicense/). 28 | 29 | - [`jdk.java.net`](https://jdk.java.net) for the current OpenJDK General Availability and Early-Access builds. 30 | 31 | Early-Access builds include the [mainline](https://github.com/openjdk/jdk/tags) JDK, Generational ZGC, Project Loom and jextract, Panama, Valhalla, etc. 32 | 33 | The [jdk.java.net-uri.properties](jdk.java.net-uri.properties) file provides a set of key-value pairs mapping OpenJDK descriptions to their download links. 34 | 35 | ### Input `release` 36 | 37 | The `release` input denotes a Java feature release number (`21`, `22`, ...) or a name of an Early-Access project (`loom`, ...). 38 | It defaults to the current General-Availability Release for the Java SE platform., which is `25` as of today. 39 | 40 | Note that websites may offer a different set of available releases. 41 | For example, `oracle.com` only offers releases of `21` and above; it does not offer Early-Access releases. 42 | 43 | Note also that websites may stop offering any release at any time. 44 | Please consult the website for details on which release is offered for how long. 45 | 46 | ### Input `version` 47 | 48 | The `version` input can be used to specify an explicit version of a Java release, such as `21.0.4`. 49 | It is set by default to `latest`. 50 | 51 | ___ 52 | 53 | > [!CAUTION] 54 | > Older versions of the JDK are provided to help developers debug issues in older systems. 55 | > **They are not updated with the latest security patches and are not recommended for use in production.** 56 | 57 | ___ 58 | 59 | ### Input `install` 60 | 61 | The `install` input enables or disables the automatic JDK installation of the downloaded JDK archive file. 62 | It is enabled by default by using `true` as its value. 63 | 64 | This action delegates to [`actions/setup-java`](https://github.com/actions/setup-java) to install the downloaded JDK archive file using default settings. 65 | Pass `false` to skip the automatic JDK installation and invoke `actions/setup-java` with your custom settings. 66 | 67 | ### Input `install-as-version` 68 | 69 | The `install-as-version` input allows overriding the value passed as `java-version` to the underlying `actions/setup-java` action. 70 | 71 | Supported values of `install-as-version` include: 72 | - `PARSE_URI` parses the computed or given URI for a valid Java version string, ex. `21.0.4`. 73 | - `HASH_URI` returns the `hashCode()` of the computed or given URI as a string, ex. `12345`. 74 | - All strings [supported by `actions/setup-java`](https://github.com/actions/setup-java#supported-version-syntax) 75 | 76 | The default value of `install-as-version` depends on the `release` input documented above: 77 | - If `release` input starts with a digit, `install-as-version` defaults to `PARSE_URI`. 78 | - If `release` input does not start with a digit, `install-as-version` defaults to `HASH_URI`. 79 | 80 | ### Input `uri` 81 | 82 | Use the `uri` input to download a JDK from the specified URI originating from a supported website. 83 | The value of inputs `website`, `release`, and `version` ignored. 84 | 85 | ## Examples for `oracle.com` 86 | 87 | The following examples use the [JDK Script Friendly URLs](https://www.oracle.com/java/technologies/jdk-script-friendly-urls/) to download and set up binaries that are made available under the [Oracle No-Fee Terms and Conditions License](https://www.java.com/freeuselicense/). 88 | 89 | ### Download and install the latest version of Oracle JDK 90 | 91 | ```yaml 92 | steps: 93 | - name: 'Set up latest Oracle JDK 25' 94 | uses: oracle-actions/setup-java@v1 95 | with: 96 | website: oracle.com 97 | release: 25 98 | ``` 99 | 100 | ### Download and install a specific version of Oracle JDK 101 | 102 | ```yaml 103 | steps: 104 | - name: 'Set up archived Oracle JDK 21.0.4' 105 | uses: oracle-actions/setup-java@v1 106 | with: 107 | website: oracle.com 108 | release: 21 109 | version: 21.0.4 110 | ``` 111 | ___ 112 | 113 | > [!CAUTION] 114 | > Older versions of the JDK are provided to help developers debug issues in older systems. 115 | > **They are not updated with the latest security patches and are not recommended for use in production.** 116 | 117 | ___ 118 | 119 | ## Examples for `jdk.java.net` 120 | 121 | The following examples download and install OpenJDK binaries that are made available under the [GNU General Public License, version 2, with the Classpath Exception](https://openjdk.java.net/legal/gplv2+ce.html). 122 | 123 | ### Download and install an OpenJDK build of a given release 124 | 125 | ```yaml 126 | steps: 127 | - name: 'Set up latest JDK N from jdk.java.net' 128 | uses: oracle-actions/setup-java@v1 129 | with: 130 | website: jdk.java.net 131 | release: N # Replace N with GA, EA, 21, 22, 23, ... 132 | ``` 133 | 134 | > [!NOTE] 135 | > This action supports two `version` symbolic modes for `release: EA` on `jdk.java.net`: 136 | > - `version: latest` updates as early as possible to the latest-and-greatest JDK build (default) 137 | > - `version: stable` updates later in the release cycle, usually when an early-access JDK build went GA 138 | 139 | ### Download and install an Early-Access build of a named OpenJDK project 140 | 141 | ```yaml 142 | steps: 143 | - name: 'Set up Early-Access build of a named project from jdk.java.net' 144 | uses: oracle-actions/setup-java@v1 145 | with: 146 | website: jdk.java.net 147 | release: loom # or panama, valhalla, ... 148 | ``` 149 | 150 | ## Supported GitHub Actions Virtual Environments 151 | 152 | All [environments](https://github.com/actions/virtual-environments#available-environments) with Java 21 pre-installed are supported. 153 | These include the following labels: `ubuntu-latest`, `macos-latest`, and `windows-latest`. 154 | 155 | ## More information 156 | 157 | Make sure to check [the announcement and the FAQ](https://inside.java/2022/03/11/setup-java/) on Inside Java. 158 | 159 | ## Status 160 | 161 | [![.github/workflows/test.yml](https://github.com/oracle-actions/setup-java/actions/workflows/test.yml/badge.svg)](https://github.com/oracle-actions/setup-java/actions/workflows/test.yml) 162 | -------------------------------------------------------------------------------- /jdk.java.net-uri.properties: -------------------------------------------------------------------------------- 1 | # 2 | # https://jdk.java.net/25/ 3 | # 4 | 25,25.0.1,linux,aarch64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-aarch64_bin.tar.gz 5 | 25,25.0.1,linux,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz 6 | 25,25.0.1,macos,aarch64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_macos-aarch64_bin.tar.gz 7 | 25,25.0.1,macos,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_macos-x64_bin.tar.gz 8 | 25,25.0.1,windows,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_windows-x64_bin.zip 9 | 25,latest,linux,aarch64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-aarch64_bin.tar.gz 10 | 25,latest,linux,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz 11 | 25,latest,macos,aarch64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_macos-aarch64_bin.tar.gz 12 | 25,latest,macos,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_macos-x64_bin.tar.gz 13 | 25,latest,windows,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_windows-x64_bin.zip 14 | ga,latest,linux,aarch64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-aarch64_bin.tar.gz 15 | ga,latest,linux,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz 16 | ga,latest,macos,aarch64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_macos-aarch64_bin.tar.gz 17 | ga,latest,macos,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_macos-x64_bin.tar.gz 18 | ga,latest,windows,x64=https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_windows-x64_bin.zip 19 | # 20 | # https://jdk.java.net/26/ 21 | # 22 | 26,26-ea+28,linux,aarch64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_linux-aarch64_bin.tar.gz 23 | 26,26-ea+28,linux,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_linux-x64_bin.tar.gz 24 | 26,26-ea+28,macos,aarch64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_macos-aarch64_bin.tar.gz 25 | 26,26-ea+28,macos,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_macos-x64_bin.tar.gz 26 | 26,26-ea+28,windows,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_windows-x64_bin.zip 27 | 26,latest,linux,aarch64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_linux-aarch64_bin.tar.gz 28 | 26,latest,linux,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_linux-x64_bin.tar.gz 29 | 26,latest,macos,aarch64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_macos-aarch64_bin.tar.gz 30 | 26,latest,macos,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_macos-x64_bin.tar.gz 31 | 26,latest,windows,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_windows-x64_bin.zip 32 | ea,stable,linux,aarch64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_linux-aarch64_bin.tar.gz 33 | ea,stable,linux,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_linux-x64_bin.tar.gz 34 | ea,stable,macos,aarch64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_macos-aarch64_bin.tar.gz 35 | ea,stable,macos,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_macos-x64_bin.tar.gz 36 | ea,stable,windows,x64=https://download.java.net/java/early_access/jdk26/28/GPL/openjdk-26-ea+28_windows-x64_bin.zip 37 | # 38 | # https://jdk.java.net/27/ 39 | # 40 | 27,27-ea+2,linux,aarch64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_linux-aarch64_bin.tar.gz 41 | 27,27-ea+2,linux,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_linux-x64_bin.tar.gz 42 | 27,27-ea+2,macos,aarch64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_macos-aarch64_bin.tar.gz 43 | 27,27-ea+2,macos,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_macos-x64_bin.tar.gz 44 | 27,27-ea+2,windows,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_windows-x64_bin.zip 45 | 27,latest,linux,aarch64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_linux-aarch64_bin.tar.gz 46 | 27,latest,linux,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_linux-x64_bin.tar.gz 47 | 27,latest,macos,aarch64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_macos-aarch64_bin.tar.gz 48 | 27,latest,macos,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_macos-x64_bin.tar.gz 49 | 27,latest,windows,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_windows-x64_bin.zip 50 | ea,latest,linux,aarch64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_linux-aarch64_bin.tar.gz 51 | ea,latest,linux,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_linux-x64_bin.tar.gz 52 | ea,latest,macos,aarch64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_macos-aarch64_bin.tar.gz 53 | ea,latest,macos,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_macos-x64_bin.tar.gz 54 | ea,latest,windows,x64=https://download.java.net/java/early_access/jdk27/2/GPL/openjdk-27-ea+2_windows-x64_bin.zip 55 | # 56 | # https://jdk.java.net/jextract/ 57 | # 58 | 25,25-jextract+2-4,linux,aarch64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_linux-aarch64_bin.tar.gz 59 | 25,25-jextract+2-4,linux,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_linux-x64_bin.tar.gz 60 | 25,25-jextract+2-4,macos,aarch64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_macos-aarch64_bin.tar.gz 61 | 25,25-jextract+2-4,macos,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_macos-x64_bin.tar.gz 62 | 25,25-jextract+2-4,windows,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_windows-x64_bin.tar.gz 63 | jextract,ea,linux,aarch64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_linux-aarch64_bin.tar.gz 64 | jextract,ea,linux,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_linux-x64_bin.tar.gz 65 | jextract,ea,macos,aarch64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_macos-aarch64_bin.tar.gz 66 | jextract,ea,macos,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_macos-x64_bin.tar.gz 67 | jextract,ea,windows,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_windows-x64_bin.tar.gz 68 | jextract,latest,linux,aarch64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_linux-aarch64_bin.tar.gz 69 | jextract,latest,linux,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_linux-x64_bin.tar.gz 70 | jextract,latest,macos,aarch64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_macos-aarch64_bin.tar.gz 71 | jextract,latest,macos,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_macos-x64_bin.tar.gz 72 | jextract,latest,windows,x64=https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_windows-x64_bin.tar.gz 73 | # 74 | # https://jdk.java.net/loom/ 75 | # 76 | 25,25-loom+1-11,linux,aarch64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_linux-aarch64_bin.tar.gz 77 | 25,25-loom+1-11,linux,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_linux-x64_bin.tar.gz 78 | 25,25-loom+1-11,macos,aarch64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_macos-aarch64_bin.tar.gz 79 | 25,25-loom+1-11,macos,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_macos-x64_bin.tar.gz 80 | 25,25-loom+1-11,windows,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_windows-x64_bin.zip 81 | loom,ea,linux,aarch64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_linux-aarch64_bin.tar.gz 82 | loom,ea,linux,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_linux-x64_bin.tar.gz 83 | loom,ea,macos,aarch64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_macos-aarch64_bin.tar.gz 84 | loom,ea,macos,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_macos-x64_bin.tar.gz 85 | loom,ea,windows,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_windows-x64_bin.zip 86 | loom,latest,linux,aarch64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_linux-aarch64_bin.tar.gz 87 | loom,latest,linux,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_linux-x64_bin.tar.gz 88 | loom,latest,macos,aarch64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_macos-aarch64_bin.tar.gz 89 | loom,latest,macos,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_macos-x64_bin.tar.gz 90 | loom,latest,windows,x64=https://download.java.net/java/early_access/loom/1/openjdk-25-loom+1-11_windows-x64_bin.zip 91 | # 92 | # https://jdk.java.net/leyden/ 93 | # 94 | 26,26-leydenpremain+1,linux,aarch64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_linux-aarch64_bin.tar.gz 95 | 26,26-leydenpremain+1,linux,x64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_linux-x64_bin.tar.gz 96 | 26,26-leydenpremain+1,macos,aarch64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_macos-aarch64_bin.tar.gz 97 | leyden,ea,linux,aarch64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_linux-aarch64_bin.tar.gz 98 | leyden,ea,linux,x64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_linux-x64_bin.tar.gz 99 | leyden,ea,macos,aarch64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_macos-aarch64_bin.tar.gz 100 | leyden,latest,linux,aarch64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_linux-aarch64_bin.tar.gz 101 | leyden,latest,linux,x64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_linux-x64_bin.tar.gz 102 | leyden,latest,macos,aarch64=https://download.java.net/java/early_access/leyden/1/openjdk-26-leydenpremain+1_macos-aarch64_bin.tar.gz 103 | # 104 | # https://jdk.java.net/valhalla/ 105 | # 106 | 26,26-jep401ea2+1-1,linux,aarch64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_linux-aarch64_bin.tar.gz 107 | 26,26-jep401ea2+1-1,linux,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_linux-x64_bin.tar.gz 108 | 26,26-jep401ea2+1-1,macos,aarch64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_macos-aarch64_bin.tar.gz 109 | 26,26-jep401ea2+1-1,macos,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_macos-x64_bin.tar.gz 110 | 26,26-jep401ea2+1-1,windows,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_windows-x64_bin.zip 111 | valhalla,ea,linux,aarch64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_linux-aarch64_bin.tar.gz 112 | valhalla,ea,linux,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_linux-x64_bin.tar.gz 113 | valhalla,ea,macos,aarch64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_macos-aarch64_bin.tar.gz 114 | valhalla,ea,macos,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_macos-x64_bin.tar.gz 115 | valhalla,ea,windows,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_windows-x64_bin.zip 116 | valhalla,latest,linux,aarch64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_linux-aarch64_bin.tar.gz 117 | valhalla,latest,linux,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_linux-x64_bin.tar.gz 118 | valhalla,latest,macos,aarch64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_macos-aarch64_bin.tar.gz 119 | valhalla,latest,macos,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_macos-x64_bin.tar.gz 120 | valhalla,latest,windows,x64=https://download.java.net/java/early_access/valhalla/26/1/openjdk-26-jep401ea2+1-1_windows-x64_bin.zip 121 | -------------------------------------------------------------------------------- /src/Download.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2024, Oracle and/or its affiliates. 3 | * 4 | * This source code is licensed under the UPL license found in the 5 | * LICENSE.txt file in the root directory of this source tree. 6 | */ 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | import static java.nio.file.StandardOpenOption.APPEND; 10 | import static java.nio.file.StandardOpenOption.CREATE; 11 | import static java.nio.file.StandardOpenOption.WRITE; 12 | 13 | import java.io.BufferedInputStream; 14 | import java.io.FileInputStream; 15 | import java.io.IOException; 16 | import java.io.OutputStream; 17 | import java.io.StringReader; 18 | import java.io.UncheckedIOException; 19 | import java.math.BigInteger; 20 | import java.net.URI; 21 | import java.net.http.HttpClient; 22 | import java.net.http.HttpRequest; 23 | import java.net.http.HttpResponse; 24 | import java.nio.file.Files; 25 | import java.nio.file.Path; 26 | import java.security.DigestOutputStream; 27 | import java.security.MessageDigest; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.Security; 30 | import java.util.ArrayDeque; 31 | import java.util.List; 32 | import java.util.Optional; 33 | import java.util.Properties; 34 | import java.util.StringJoiner; 35 | import java.util.TreeMap; 36 | import java.util.regex.Pattern; 37 | 38 | /** Download a JDK build. */ 39 | public class Download { 40 | /** Main entry-point. */ 41 | public static void main(String... args) { 42 | main(Boolean.getBoolean(/*-D*/ "ry-run"), args); 43 | } 44 | 45 | /** Entry-point also used by tests. */ 46 | static void main(boolean dryRun, String... args) { 47 | // Pre-allocate action outputs 48 | var outputs = new TreeMap(); 49 | outputs.put("archive", "NOT-SET"); 50 | outputs.put("version", "NOT-SET"); 51 | try { 52 | if (args.length == 0) { 53 | throw new Error("Usage: Download URI or WEBSITE RELEASE VERSION"); 54 | } 55 | var deque = new ArrayDeque<>(List.of(args)); 56 | var first = deque.removeFirst(); // URI or WEBSITE 57 | 58 | // Determine website from first argument 59 | var website = 60 | Website.find(first).orElseThrow(() -> new Error("Could not find website for " + first)); 61 | GitHub.debug("website: " + website); 62 | 63 | // Create JDK descriptor 64 | var jdk = 65 | new JDK( 66 | deque.isEmpty() ? "ga" : deque.removeFirst().toLowerCase(), 67 | deque.isEmpty() ? "latest" : deque.removeFirst().toLowerCase(), 68 | deque.isEmpty() ? JDK.computeOsName() : deque.removeFirst(), 69 | deque.isEmpty() ? JDK.computeOsArch() : deque.removeFirst(), 70 | deque.isEmpty() ? JDK.computeFileType() : deque.removeFirst()); 71 | GitHub.debug("jdk: " + jdk); 72 | 73 | // Select or find URI based on the JDK descriptor 74 | var uri = 75 | args.length == 1 76 | ? first 77 | : website.findUri(jdk).orElseThrow(() -> new Error("Could not find URI of " + jdk)); 78 | GitHub.debug("uri: " + uri); 79 | if (!(uri.endsWith(".tar.gz") || uri.endsWith(".zip"))) { 80 | throw new IllegalArgumentException("URI must end with `.tar.gz` or `.zip`: " + uri); 81 | } 82 | 83 | // Emit warning when using an archived JDK build 84 | if (website.isArchivedUri(uri)) { 85 | GitHub.warn( 86 | """ 87 | JDK resolved to an archived build! 88 | These older versions of the JDK are provided to help developers debug issues in older systems. 89 | They are not updated with the latest security patches and are not recommended for use in production. 90 | """); 91 | } 92 | 93 | // Acquire JDK archive 94 | var archive = website.computeArchivePath(uri); 95 | GitHub.debug("archive: " + archive); 96 | var downloader = new Downloader(archive, uri); 97 | if (website.isMovingResourceUri(uri)) { 98 | downloader.checkSizeAndDeleteIfDifferent(); 99 | } 100 | downloader.downloadArchive(dryRun); 101 | downloader.verifyChecksums(website.getChecksum(uri)); 102 | System.out.printf("Archive %s in %s%n", archive.getFileName(), archive.getParent().toUri()); 103 | 104 | // Set outputs 105 | outputs.put("archive", archive.toString()); 106 | var digit = Character.isDigit(jdk.release.charAt(0)); 107 | outputs.put("version", website.computeVersionString(uri, digit ? "PARSE_URI" : "HASH_URI")); 108 | } catch (Exception exception) { 109 | GitHub.error("Error detected: " + exception); 110 | throw new Error(exception); // ensure non-zero result code is returned 111 | } finally { 112 | if (dryRun) { 113 | System.out.println("Dry-run of run with " + List.of(args)); 114 | for (var output : outputs.entrySet()) { 115 | System.out.println(" - " + output.getKey() + '=' + output.getValue()); 116 | } 117 | } else { 118 | outputs.forEach(GitHub::setOutput); 119 | } 120 | } 121 | } 122 | 123 | record JDK(String release, String version, String os, String arch, String type) { 124 | 125 | static String computeOsName() { 126 | var name = System.getProperty("os.name").toLowerCase(); 127 | if (name.contains("win")) return "windows"; 128 | if (name.contains("mac")) return "macos"; 129 | return "linux"; 130 | } 131 | 132 | static String computeOsArch() { 133 | var arch = System.getProperty("os.arch", "x64"); 134 | if (arch.equals("amd64")) return "x64"; 135 | if (arch.equals("x86_64")) return "x64"; 136 | return arch; 137 | } 138 | 139 | static String computeFileType() { 140 | var name = System.getProperty("os.name").toLowerCase(); 141 | return name.contains("win") ? "zip" : "tar.gz"; 142 | } 143 | } 144 | 145 | /** Download helper. */ 146 | static class Downloader { 147 | 148 | final Path archive; 149 | final String uri; 150 | final Browser browser; 151 | 152 | Downloader(Path archive, String uri) { 153 | this.archive = archive; 154 | this.uri = uri; 155 | this.browser = new Browser(); 156 | } 157 | 158 | void checkSizeAndDeleteIfDifferent() throws Exception { 159 | if (Files.notExists(archive)) return; 160 | var cachedSize = Files.size(archive); 161 | GitHub.debug("Cached size: " + cachedSize); 162 | var remoteSize = browser.head(uri).headers().firstValueAsLong("content-length").orElse(-1); 163 | GitHub.debug("Remote size: " + remoteSize); 164 | if (cachedSize == remoteSize) return; 165 | Files.delete(archive); 166 | } 167 | 168 | void downloadArchive(boolean dryRun) throws Exception { 169 | if (Files.exists(archive)) return; 170 | var head = browser.head(uri); 171 | GitHub.debug(head.toString()); 172 | if (dryRun) { 173 | return; 174 | } 175 | int retry = 0; 176 | while (true) { 177 | try { 178 | GitHub.debug("Downloading " + uri); 179 | var response = browser.download(uri, archive); 180 | GitHub.debug(response.toString()); 181 | return; 182 | } catch (IOException exception) { 183 | var message = Optional.ofNullable(exception.getMessage()).orElseGet(exception::toString); 184 | if (++retry == 3) { 185 | GitHub.error("Download failed due to: " + message); 186 | throw exception; 187 | } 188 | var seconds = retry * 10; 189 | GitHub.warn(String.format("Retrying in %d seconds due to: %s", seconds, message)); 190 | //noinspection BusyWait 191 | Thread.sleep(seconds * 1000L); 192 | } 193 | } 194 | } 195 | 196 | void verifyChecksums(String checksum) throws Exception { 197 | if (Files.notExists(archive)) return; 198 | var cached = computeChecksum(archive); 199 | GitHub.debug("Cached checksum: " + cached); 200 | 201 | var remoteChecksum = findRemoteChecksum(checksum); 202 | if (remoteChecksum.isEmpty()) { 203 | GitHub.warn("Checksum not available for: " + uri); 204 | } else { 205 | var remote = remoteChecksum.get(); 206 | GitHub.debug("Remote checksum: " + remote); 207 | if (cached.equals(remote)) { 208 | return; 209 | } 210 | } 211 | var message = "Checksum verification failed, deleting cached archive"; 212 | Files.delete(archive); 213 | GitHub.error(message); 214 | throw new AssertionError(message); 215 | } 216 | 217 | String computeChecksum(Path path) { 218 | try { 219 | var md = MessageDigest.getInstance("SHA-256"); 220 | try (var input = new BufferedInputStream(new FileInputStream(path.toFile())); 221 | var output = new DigestOutputStream(OutputStream.nullOutputStream(), md)) { 222 | input.transferTo(output); 223 | } 224 | var length = md.getDigestLength() * 2; 225 | return String.format("%0" + length + "x", new BigInteger(1, md.digest())); 226 | } catch (IOException exception) { 227 | throw new UncheckedIOException(exception); 228 | } catch (NoSuchAlgorithmException exception) { 229 | var algorithms = Security.getAlgorithms("MessageDigest"); 230 | throw new IllegalArgumentException(exception + ": " + algorithms); 231 | } 232 | } 233 | 234 | Optional findRemoteChecksum(String checksum) throws Exception { 235 | if (!checksum.startsWith("https://")) return Optional.of(checksum); 236 | if (browser.head(checksum).statusCode() == 200) return Optional.of(browser.browse(checksum)); 237 | return Optional.empty(); 238 | } 239 | } 240 | 241 | /** 242 | * GitHub Actions helper. 243 | * 244 | * @see Workflow 246 | * commands for GitHub Actions 247 | */ 248 | static class GitHub { 249 | /** Sets an action's output parameter. */ 250 | static void setOutput(String name, Object value) { 251 | if (name.isBlank() || value.toString().isBlank()) { // implicit null checks included 252 | throw new IllegalArgumentException("name or value are blank: " + name + "=" + value); 253 | } 254 | var githubOutput = System.getenv("GITHUB_OUTPUT"); 255 | if (githubOutput == null) { 256 | throw new AssertionError("No such environment variable: GITHUB_OUTPUT"); 257 | } 258 | try { 259 | var file = Path.of(githubOutput); 260 | if (file.getParent() != null) Files.createDirectories(file.getParent()); 261 | var lines = (name + "=" + value).lines().toList(); 262 | if (lines.size() != 1) { 263 | throw new UnsupportedOperationException("Multiline strings are no supported"); 264 | } 265 | debug("Write output %s to %s".formatted(lines, file)); 266 | Files.write(file, lines, UTF_8, CREATE, APPEND, WRITE); 267 | } catch (IOException exception) { 268 | throw new UncheckedIOException(exception); 269 | } 270 | } 271 | 272 | /** Creates a debug message and prints the message to the log. */ 273 | static void debug(String message) { 274 | System.out.printf("::debug::%s%n", message.replaceAll("\\R", "%0A")); 275 | } 276 | 277 | /** Creates a warning message and prints the message to the log. */ 278 | static void warn(String message) { 279 | System.out.printf("::warning::%s%n", message.replaceAll("\\R", "%0A")); 280 | } 281 | 282 | /** Creates an error message and prints the message to the log. */ 283 | static void error(String message) { 284 | System.out.printf("::error::%s%n", message.replaceAll("\\R", "%0A")); 285 | } 286 | } 287 | 288 | /** HTTP-related helper. */ 289 | static class Browser { 290 | final HttpClient client; 291 | 292 | Browser() { 293 | this.client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); 294 | } 295 | 296 | String browse(String uri) throws Exception { 297 | var request = HttpRequest.newBuilder(URI.create(uri)).build(); 298 | return client.send(request, HttpResponse.BodyHandlers.ofString()).body(); 299 | } 300 | 301 | HttpResponse download(String uri, Path file) throws Exception { 302 | var parent = file.getParent(); 303 | if (parent != null) Files.createDirectories(parent); 304 | var request = HttpRequest.newBuilder(URI.create(uri)).build(); 305 | return client.send(request, HttpResponse.BodyHandlers.ofFile(file)); 306 | } 307 | 308 | HttpResponse head(String uri) throws Exception { 309 | var request = 310 | HttpRequest.newBuilder(URI.create(uri)) 311 | .method("HEAD", HttpRequest.BodyPublishers.noBody()) 312 | .build(); 313 | return client.send(request, HttpResponse.BodyHandlers.discarding()); 314 | } 315 | } 316 | 317 | /** A website hosting JDK builds. */ 318 | interface Website { 319 | 320 | /** Try to instantiate a website implementation for the given hint. */ 321 | static Optional find(String hint) { 322 | if (hint.equals(OracleComWebsite.NAME) || hint.startsWith(OracleComWebsite.URI_PREFIX)) { 323 | return Optional.of(new OracleComWebsite()); 324 | } 325 | if (hint.equals(JavaNetWebsite.NAME) || hint.startsWith(JavaNetWebsite.URI_PREFIX)) { 326 | return Optional.of(new JavaNetWebsite()); 327 | } 328 | return Optional.empty(); 329 | } 330 | 331 | Optional findUri(JDK jdk); 332 | 333 | default Path computeArchivePath(String uri) { 334 | var file = uri.substring(uri.lastIndexOf('/') + 1); 335 | var home = System.getProperty("user.home"); 336 | var hash = Integer.toHexString(uri.hashCode()); 337 | var cache = Path.of(home, ".oracle-actions", "setup-java", hash); 338 | return cache.resolve(file); 339 | } 340 | 341 | default String computeVersionString(String uri, String defaultVersion) { 342 | var property = System.getProperty("install-as-version"); 343 | GitHub.debug("install-as-version: " + property); 344 | var version = property == null || property.isBlank() ? defaultVersion : property; 345 | return switch (version) { 346 | case "PARSE_URI" -> parseVersion(uri).orElse("UNKNOWN-VERSION"); 347 | case "HASH_URI" -> Integer.toString(Math.abs(uri.hashCode())); 348 | default -> version; 349 | }; 350 | } 351 | 352 | /** Try to parse version information from the given uri. */ 353 | default Optional parseVersion(String uri) { 354 | for (var versionPattern : parseVersionPatterns()) { 355 | var matcher = versionPattern.matcher(uri); 356 | if (matcher.matches()) { 357 | // "$FEATURE.$INTERIM.$UPDATE.$PATCH" 358 | var version = Runtime.Version.parse(matcher.group(1)); 359 | var joiner = new StringJoiner("."); 360 | joiner.add(String.valueOf(version.feature())); 361 | if (version.interim() != 0 || version.update() != 0) { 362 | joiner.add(String.valueOf(version.interim())); 363 | if (version.update() != 0) { 364 | joiner.add(String.valueOf(version.update())); 365 | } 366 | } 367 | return Optional.of(joiner.toString()); 368 | } 369 | } 370 | return Optional.empty(); 371 | } 372 | 373 | /** A list of patterns with each has at least one version-defining capture group. */ 374 | default List parseVersionPatterns() { 375 | return List.of(); 376 | } 377 | 378 | /** Test the given uri for potentially pointing to different resources over time. */ 379 | default boolean isMovingResourceUri(String uri) { 380 | return false; 381 | } 382 | 383 | /** Test the given uri for pointing to an archived JDK build. */ 384 | default boolean isArchivedUri(String uri) { 385 | return false; 386 | } 387 | 388 | /** The checksum for the given uri, possibly a uri pointing to a remote file. */ 389 | default String getChecksum(String uri) { 390 | return uri + ".sha256"; 391 | } 392 | } 393 | 394 | /** JDK builds hosted at {@code https://oracle.com}. */ 395 | static class OracleComWebsite implements Website { 396 | 397 | static String NAME = "oracle.com"; 398 | static String URI_PREFIX = "https://download.oracle.com/java/"; 399 | 400 | @Override 401 | public List parseVersionPatterns() { 402 | return List.of(Pattern.compile("\\Q" + URI_PREFIX + "\\E.+?/jdk-([\\d.]+).+")); 403 | } 404 | 405 | @Override 406 | public boolean isMovingResourceUri(String uri) { 407 | return uri.contains("/latest/"); 408 | } 409 | 410 | @Override 411 | public boolean isArchivedUri(String uri) { 412 | return uri.contains("/archive/"); 413 | } 414 | 415 | @Override 416 | public Optional findUri(JDK jdk) { 417 | if (Integer.parseInt(jdk.release) < 17) return Optional.empty(); 418 | if (jdk.version.equals("latest")) return Optional.of(computeLatestUri(jdk)); 419 | return Optional.of(computeArchiveUri(jdk)); 420 | } 421 | 422 | String computeLatestUri(JDK jdk) { 423 | var format = URI_PREFIX + "%s/latest/jdk-%s_%s-%s_bin.%s"; 424 | return String.format(format, jdk.release, jdk.release, jdk.os, jdk.arch, jdk.type); 425 | } 426 | 427 | String computeArchiveUri(JDK jdk) { 428 | var format = URI_PREFIX + "%s/archive/jdk-%s_%s-%s_bin.%s"; 429 | return String.format(format, jdk.release, jdk.version, jdk.os, jdk.arch, jdk.type); 430 | } 431 | } 432 | 433 | /** JDK builds hosted at {@code https://jdk.java.net}. */ 434 | static class JavaNetWebsite implements Website { 435 | static String NAME = "jdk.java.net"; 436 | static String URI_PREFIX = "https://download.java.net"; 437 | static /*lazy*/ Properties URI_MAPPING = null; 438 | 439 | @Override 440 | public List parseVersionPatterns() { 441 | return List.of(Pattern.compile("\\Q" + URI_PREFIX + "\\E.+?/openjdk-([\\d.]+).+")); 442 | } 443 | 444 | @Override 445 | public synchronized Optional findUri(JDK jdk) { 446 | var key = 447 | new StringJoiner(",") 448 | .add(jdk.release) 449 | .add(jdk.version) 450 | .add(jdk.os) 451 | .add(jdk.arch) 452 | .toString(); 453 | if (URI_MAPPING == null) { 454 | try { 455 | URI_MAPPING = new Properties(); 456 | var browser = new Browser(); 457 | var s = 458 | browser.browse( 459 | "https://raw.githubusercontent.com" 460 | + "/oracle-actions/setup-java/main" // user/repo/branch 461 | + "/jdk.java.net-uri.properties"); 462 | URI_MAPPING.load(new StringReader(s)); 463 | } catch (Exception exception) { 464 | GitHub.warn("Caught exception: " + exception); 465 | return Optional.empty(); 466 | } 467 | } 468 | var value = URI_MAPPING.getProperty(key); 469 | if (value == null) { 470 | GitHub.warn("No URI mapped for key: " + key); 471 | } 472 | return Optional.ofNullable(value); 473 | } 474 | } 475 | } 476 | --------------------------------------------------------------------------------