diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 000000000000..a6530be6806e --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,25 @@ +# see https://s.apache.org/asfyaml +github: + description: "Apache Maven core" + homepage: https://maven.apache.org/ref/current + labels: + - java + - build-management + - apache-maven + - maven + - hacktoberfest + enabled_merge_buttons: + squash: true + merge: false + rebase: true + autolink_jira: + - MNG + pull_requests: + del_branch_on_merge: true + features: + issues: true +notifications: + commits: commits@maven.apache.org + issues: issues@maven.apache.org + pullrequests: issues@maven.apache.org + jira_options: link label diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..93ac858e2e00 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# + +# Change maven code style +de19cfcd2bc8e774818d87472e8e64dc37c0b93d diff --git a/.gitattributes b/.gitattributes index 3bb3b5ea8a4d..150d31a7f0f4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,5 @@ *.css text *.js text *.sql text +*.jar binary +*.war binary diff --git a/.github/ISSUE_TEMPLATE/BUG.yml b/.github/ISSUE_TEMPLATE/BUG.yml new file mode 100644 index 000000000000..699181ff118f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG.yml @@ -0,0 +1,48 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 +# +# http://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. +# + +# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema + +name: Bug Report +description: File a bug report +labels: ["bug"] + +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report. + + Simple fixes in single PRs do not require issues. + + **Do you use the latest project version?** + + - type: input + id: version + attributes: + label: Affected version + validations: + required: true + + - type: textarea + id: message + attributes: + label: Bug description + validations: + required: true + + diff --git a/.github/ISSUE_TEMPLATE/FEATURE.yml b/.github/ISSUE_TEMPLATE/FEATURE.yml new file mode 100644 index 000000000000..ddfd1a45e4db --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE.yml @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 +# +# http://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. +# + +# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema + +name: Feature request +description: File a proposal for new feature, improvement +labels: ["enhancement"] + +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this new feature, improvement proposal. + + - type: textarea + id: message + attributes: + label: New feature, improvement proposal + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..b27d66331198 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,26 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 +# +# http://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. +# + +# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser + +blank_issues_enabled: false + +contact_links: + + - name: Project Mailing Lists + url: https://maven.apache.org/mailing-lists.html + about: Please ask a question or discuss here diff --git a/.github/ci-extensions.xml b/.github/ci-extensions.xml new file mode 100644 index 000000000000..5c6a0b437805 --- /dev/null +++ b/.github/ci-extensions.xml @@ -0,0 +1,26 @@ + + + + + eu.maveniverse.maven.mimir + extension + 0.7.8 + + \ No newline at end of file diff --git a/.github/ci-mimir-daemon.properties b/.github/ci-mimir-daemon.properties new file mode 100644 index 000000000000..86a84b6ac58d --- /dev/null +++ b/.github/ci-mimir-daemon.properties @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 +# +# http://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. +# + +# Mimir Daemon properties + +# Disable JGroups; we don't want/use LAN cache sharing +mimir.jgroups.enabled=false \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..1576b20f1381 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,66 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 +# +# http://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. +# +version: 2 +updates: + + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + target-branch: "maven-4.0.x" + labels: + - "mvn40" + - "dependencies" + + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + target-branch: "maven-3.9.x" + labels: + - "mvn3" + - "dependencies" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + target-branch: "maven-4.0.x" + labels: + - "mvn40" + - "dependencies" + - "github_actions" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + target-branch: "maven-3.9.x" + labels: + - "mvn3" + - "dependencies" + - "github_actions" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2208cd74948c..6e422e4a09c7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,30 +1,25 @@ -Following this checklist to help us incorporate your +Following this checklist to help us incorporate your contribution quickly and easily: - - [ ] Make sure there is a [JIRA issue](https://issues.apache.org/jira/browse/MNG) filed - for the change (usually before you start working on it). Trivial changes like typos do not - require a JIRA issue. Your pull request should address just this issue, without - pulling in other changes. - - [ ] Each commit in the pull request should have a meaningful subject line and body. - - [ ] Format the pull request title like `[MNG-XXX] - Fixes bug in ApproximateQuantiles`, - where you replace `MNG-XXX` with the appropriate JIRA issue. Best practice - is to use the JIRA issue title in the pull request title and in the first line of the - commit message. - - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. - - [ ] Run `mvn clean verify` to make sure basic checks pass. A more thorough check will - be performed on your pull request automatically. - - [ ] You have run the [Core IT][core-its] successfully. +- [ ] Your pull request should address just one issue, without pulling in other changes. +- [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. +- [ ] Each commit in the pull request should have a meaningful subject line and body. + Note that commits might be squashed by a maintainer on merge. +- [ ] Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. + This may not always be possible but is a best-practice. +- [ ] Run `mvn verify` to make sure basic checks pass. + A more thorough check will be performed on your pull request automatically. +- [ ] You have run the [Core IT][core-its] successfully. If your pull request is about ~20 lines of code you don't need to sign an [Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf) if you are unsure please ask on the developers list. -To make clear that you license your contribution under +To make clear that you license your contribution under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) you have to acknowledge this by using the following check-box. - - [ ] I hereby declare this contribution to be licenced under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) - - - [ ] In any other case, please file an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf). +- [ ] I hereby declare this contribution to be licenced under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) +- [ ] In any other case, please file an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf). [core-its]: https://maven.apache.org/core-its/core-it-suite/ diff --git a/.github/release-drafter-3.x.yml b/.github/release-drafter-3.x.yml new file mode 100644 index 000000000000..da2e7a556cb7 --- /dev/null +++ b/.github/release-drafter-3.x.yml @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +_extends: maven-gh-actions-shared:.github/release-drafter.yml +tag-template: maven-$RESOLVED_VERSION diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000000..add27b1def8a --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +_extends: maven-gh-actions-shared +tag-template: maven-$RESOLVED_VERSION + +include-pre-releases: true +prerelease: true diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 000000000000..e6d585e3688a --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,249 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +name: Java CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +# allow single build per branch or PR +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# clear all permissions for GITHUB_TOKEN +permissions: {} + +jobs: + initial-build: + runs-on: ubuntu-latest + steps: + - name: Set up JDK + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5 + with: + java-version: 17 + distribution: 'temurin' + + - name: Checkout maven + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4 + with: + persist-credentials: false + + - name: Prepare Mimir + shell: bash + run: | + mkdir -p ~/.mimir + cp .github/ci-extensions.xml ~/.m2/extensions.xml + cp .github/ci-mimir-daemon.properties ~/.mimir/daemon.properties + + - name: Handle Mimir caches + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + with: + path: ~/.mimir/local + key: mimir-${{ runner.os }}-initial-${{ hashFiles('**/pom.xml') }} + restore-keys: | + mimir-${{ runner.os }}-initial- + mimir-${{ runner.os }}- + + - name: Set up Maven + shell: bash + run: mvn --errors --batch-mode --show-version org.apache.maven.plugins:maven-wrapper-plugin:3.3.2:wrapper "-Dmaven=4.0.0-rc-3" + + - name: Build Maven distributions + shell: bash + run: ./mvnw verify -e -B -V + + - name: List contents of target directory + shell: bash + run: ls -la apache-maven/target + + - name: Upload Maven distributions + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: maven-distributions + path: | + apache-maven/target/apache-maven*.zip + apache-maven/target/apache-maven*.tar.gz + + full-build: + needs: initial-build + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + java: ['17', '21', '24'] + steps: + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + + - name: Install Graphviz (MacOS) + if: runner.os == 'macOS' + run: brew install graphviz + + - name: Install Graphviz (Ubuntu) + if: runner.os == 'Linux' + run: sudo apt-get install graphviz + + - name: Install Graphviz (Windows) + if: runner.os == 'Windows' + run: choco install graphviz + + - name: Checkout maven + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4 + with: + persist-credentials: false + + - name: Prepare Mimir + shell: bash + run: | + mkdir -p ~/.m2 + mkdir -p ~/.mimir + cp .github/ci-extensions.xml ~/.m2/extensions.xml + cp .github/ci-mimir-daemon.properties ~/.mimir/daemon.properties + + - name: Handle Mimir caches + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + with: + path: ~/.mimir/local + key: mimir-${{ runner.os }}-full-${{ hashFiles('**/pom.xml') }} + restore-keys: | + mimir-${{ runner.os }}-full- + mimir-${{ runner.os }}- + + - name: Download Maven distribution + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4 + with: + name: maven-distributions + path: maven-dist + + - name: List downloaded files + shell: bash + run: ls -la maven-dist + + - name: Extract Maven distribution + shell: bash + run: | + mkdir -p maven-local + if [ "${{ runner.os }}" = "Windows" ]; then + unzip maven-dist/apache-maven-*-bin.zip -d maven-local + # Get the name of the extracted directory + MAVEN_DIR=$(ls maven-local) + # Move contents up one level + mv "maven-local/$MAVEN_DIR"/* maven-local/ + rm -r "maven-local/$MAVEN_DIR" + else + tar xzf maven-dist/apache-maven-*-bin.tar.gz -C maven-local --strip-components 1 + fi + echo "MAVEN_HOME=$PWD/maven-local" >> $GITHUB_ENV + echo "$PWD/maven-local/bin" >> $GITHUB_PATH + + - name: Build with downloaded Maven + shell: bash + run: mvn verify -Papache-release -Dgpg.skip=true -e -B -V + + - name: Build site with downloaded Maven + shell: bash + run: mvn site -e -B -V -Preporting + + - name: Upload test artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + if: failure() || cancelled() + with: + name: ${{ github.run_number }}-full-build-artifact-${{ runner.os }}-${{ matrix.java }} + path: '**/target/surefire-reports/*' + + integration-tests: + needs: initial-build + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + java: ['17', '21', '24'] + steps: + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + + - name: Checkout maven + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4 + with: + persist-credentials: false + + - name: Prepare Mimir + shell: bash + run: | + mkdir -p ~/.m2 + mkdir -p ~/.mimir + cp .github/ci-extensions.xml ~/.m2/extensions.xml + cp .github/ci-mimir-daemon.properties ~/.mimir/daemon.properties + + - name: Handle Mimir caches + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + with: + path: ~/.mimir/local + key: mimir-${{ runner.os }}-its-${{ hashFiles('**/pom.xml') }} + restore-keys: | + mimir-${{ runner.os }}-its- + mimir-${{ runner.os }}- + + - name: Download Maven distribution + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4 + with: + name: maven-distributions + path: maven-dist + + - name: List downloaded files + shell: bash + run: ls -la maven-dist + + - name: Extract Maven distribution + shell: bash + run: | + mkdir -p maven-local + if [ "${{ runner.os }}" = "Windows" ]; then + unzip maven-dist/apache-maven-*-bin.zip -d maven-local + # Get the name of the extracted directory + MAVEN_DIR=$(ls maven-local) + # Move contents up one level + mv "maven-local/$MAVEN_DIR"/* maven-local/ + rm -r "maven-local/$MAVEN_DIR" + else + tar xzf maven-dist/apache-maven-*-bin.tar.gz -C maven-local --strip-components 1 + fi + echo "MAVEN_HOME=$PWD/maven-local" >> $GITHUB_ENV + echo "$PWD/maven-local/bin" >> $GITHUB_PATH + + - name: Build Maven and ITs and run them + shell: bash + run: mvn verify -e -B -V -Prun-its,mimir + + - name: Upload test artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + if: failure() || cancelled() + with: + name: ${{ github.run_number }}-integration-test-artifact-${{ runner.os }}-${{ matrix.java }} + path: ./its/core-it-suite/target/test-classes/ diff --git a/.github/workflows/pr-automation.yml b/.github/workflows/pr-automation.yml new file mode 100644 index 000000000000..530759572d8f --- /dev/null +++ b/.github/workflows/pr-automation.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +name: PR Automation +on: + pull_request_target: + types: + - closed + +jobs: + pr-automation: + name: PR Automation + uses: apache/maven-gh-actions-shared/.github/workflows/pr-automation.yml@v4 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 000000000000..96eaa60a0f66 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +name: Release Drafter +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + update_release_draft: + uses: apache/maven-gh-actions-shared/.github/workflows/release-drafter.yml@v4 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..a3d320595715 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +name: Stale + +on: + schedule: + - cron: '29 3 * * *' + issue_comment: + types: [ 'created' ] + +jobs: + stale: + uses: 'apache/maven-gh-actions-shared/.github/workflows/stale.yml@v4' diff --git a/.gitignore b/.gitignore index f79c9285cd4c..4e85f56fb4ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,24 @@ -target/ +**/target/** .project .classpath .settings/ .svn/ -bin/ + # Intellij *.ipr *.iml .idea -out/ -.DS_Store +!.idea/icon.png /bootstrap /dependencies.xml .java-version +.checkstyle +.factorypath +repo/ + +# VSCode +.vscode/ + +# Mac +.DS_Store + diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 000000000000..55035f1855aa Binary files /dev/null and b/.idea/icon.png differ diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 000000000000..c6f922ecc298 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +-DsessionRootDirectory=${session.rootDirectory} \ No newline at end of file diff --git a/.mvn/readme.txt b/.mvn/readme.txt new file mode 100644 index 000000000000..c2d3655427cd --- /dev/null +++ b/.mvn/readme.txt @@ -0,0 +1 @@ +The .mvn directory is needed to be able to use the ${maven.multiModuleProjectDirectory} property. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9989fb163b29..a5b24766659e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -Contributing to Apache Maven +Contributing to Apache Maven ====================== You have found a bug or you have an idea for a cool new feature? Contributing @@ -29,11 +29,10 @@ which might be interesting to read and for further discussion. Getting Started --------------- -+ Make sure you have a [JIRA account](https://issues.apache.org/jira/). + Make sure you have a [GitHub account](https://github.com/signup/free). -+ If you're planning to implement a new feature, it makes sense to discuss your changes ++ If you're planning to implement a new feature, it makes sense to discuss your changes on the [dev list](https://maven.apache.org/mailing-lists.html) first. - This way you can make sure you're not wasting your time on something that isn't + This way you can make sure you're not wasting your time on something that isn't considered to be in Apache Maven's scope. + Submit a ticket for your issue, assuming one does not already exist. + Clearly describe the issue, including steps to reproduce when it is a bug. @@ -52,28 +51,15 @@ There are some guidelines which will make applying PRs easier for us: + Respect the original code style: by using the same [codestyle][code-style], patches should only highlight the actual difference, not being disturbed by any formatting issues: + Only use spaces for indentation. - + Create minimal diffs - disable on save actions like reformat source code or organize imports. + + Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted, create a separate PR for this change. + Check for unnecessary whitespace with `git diff --check` before committing. -+ Make sure your commit messages are in the proper format. Your commit message should contain the key of the JIRA issue. -``` -[MNG-XXX] - Subject of the JIRA Ticket - Optional supplemental description. -``` + Make sure you have added the necessary tests (JUnit/[Core IT tests][core-it]) for your changes. + Run all the tests with `mvn -Prun-its verify` to assure nothing else was accidentally broken. + Submit a pull request to the repository in the Apache organization. -+ Update your JIRA ticket and include a link to the pull request in the ticket. If you plan to contribute on a regular basis, please consider filing a [contributor license agreement][cla]. -Making Trivial Changes ----------------------- - -For changes of a trivial nature to comments and documentation, it is not always -necessary to create a new ticket in JIRA. In this case, it is appropriate to -start the first line of a commit with '(doc)' instead of a ticket number. - Additional Resources -------------------- @@ -81,9 +67,10 @@ Additional Resources + [Apache Maven JIRA project page](https://issues.apache.org/jira/projects/MNG/) + [Contributor License Agreement][cla] + [General GitHub documentation](https://help.github.com/) -+ [GitHub pull request documentation](https://help.github.com/send-pull-requests/) -+ [Apache Maven Twitter Account](https://twitter.com/ASFMavenProject) -+ #Maven IRC channel on freenode.org ++ [GitHub pull request documentation](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) ++ [Apache Maven X Account](https://x.com/ASFMavenProject) ++ [Apache Maven Bluesky Account](https://bsky.app/profile/maven.apache.org) ++ [Apache Maven Mastodon Account](https://mastodon.social/deck/@ASFMavenProject@fosstodon.org) [dev-ml-list]: https://maven.apache.org/mailing-lists.html [code-style]: https://maven.apache.org/developers/conventions/code.html diff --git a/Jenkinsfile b/Jenkinsfile index 5d5d998c0398..a1bc86896e5d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,173 +1,62 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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. - */ - -properties([buildDiscarder(logRotator(artifactNumToKeepStr: '5', numToKeepStr: env.BRANCH_NAME=='master'?'5':'1'))]) - -def buildOs = 'linux' -def buildJdk = '8' -def buildMvn = '3.6.0' -def runITsOses = ['linux', 'windows'] -def runITsJdks = ['7', '8', '11','12'] -def runITsMvn = '3.6.0' -def runITscommand = "mvn clean install -Prun-its,embedded -B -U -V" // -DmavenDistro=... -Dmaven.test.failure.ignore=true -def tests - -try { - -def osNode = jenkinsEnv.labelForOS(buildOs) -node(jenkinsEnv.nodeSelection(osNode)) { - dir('build') { - stage('Checkout') { - checkout scm - } - - def WORK_DIR=pwd() - def MAVEN_GOAL='verify' - - stage('Configure deploy') { - if (env.BRANCH_NAME == 'master'){ - MAVEN_GOAL='deploy' - } - } - - stage('Build / Unit Test') { - String jdkName = jenkinsEnv.jdkFromVersion(buildOs, buildJdk) - String mvnName = jenkinsEnv.mvnFromVersion(buildOs, buildMvn) - withMaven(jdk: jdkName, maven: mvnName, mavenLocalRepo:"${WORK_DIR}/.repository", options:[ - artifactsPublisher(disabled: false), - junitPublisher(ignoreAttachments: false), - findbugsPublisher(disabled: false), - openTasksPublisher(disabled: false), - dependenciesFingerprintPublisher(), - invokerPublisher(), - pipelineGraphPublisher() - ]) { - sh "mvn clean ${MAVEN_GOAL} -B -U -e -fae -V -Dmaven.test.failure.ignore=true" - } - dir ('apache-maven/target') { - sh "mv apache-maven-*-bin.zip apache-maven-dist.zip" - stash includes: 'apache-maven-dist.zip', name: 'dist' +#!groovy + +pipeline { + agent none + // save some io during the build + options { + skipDefaultCheckout() + durabilityHint('PERFORMANCE_OPTIMIZED') + disableRestartFromStage() + } + stages { + stage("Build / Test - JDK17") { + agent { node { label 'ubuntu' } } + steps { + timeout(time: 210, unit: 'MINUTES') { + checkout scm + mavenBuild("jdk_17_latest", "") + script { + properties([buildDiscarder(logRotator(artifactNumToKeepStr: '5', numToKeepStr: isDeployedBranch() ? '30' : '5'))]) + if (isDeployedBranch()) { + withEnv(["JAVA_HOME=${tool "jdk_17_latest"}", + "PATH+MAVEN=${ tool "jdk_17_latest" }/bin:${tool "maven_3_latest"}/bin", + "MAVEN_OPTS=-Xms4G -Xmx4G -Djava.awt.headless=true"]) { + sh "mvn clean deploy -DdeployAtEnd=true -B" + } } + } } - - tests = resolveScm source: [$class: 'GitSCMSource', credentialsId: '', id: '_', remote: 'https://gitbox.apache.org/repos/asf/maven-integration-testing.git', traits: [[$class: 'jenkins.plugins.git.traits.BranchDiscoveryTrait'], [$class: 'GitToolSCMSourceTrait', gitTool: 'Default']]], targets: [BRANCH_NAME, 'master'] + } } + } } -Map runITsTasks = [:] -for (String os in runITsOses) { - for (def jdk in runITsJdks) { - String osLabel = jenkinsEnv.labelForOS(os); - String jdkName = jenkinsEnv.jdkFromVersion(os, "${jdk}") - String mvnName = jenkinsEnv.mvnFromVersion(os, "${runITsMvn}") - echo "OS: ${os} JDK: ${jdk} => Label: ${osLabel} JDK: ${jdkName}" - - String stageId = "${os}-jdk${jdk}" - String stageLabel = "Run ITs ${os.capitalize()} Java ${jdk}" - runITsTasks[stageId] = { - node(jenkinsEnv.nodeSelection(osLabel)) { - stage("${stageLabel}") { - echo "NODE_NAME = ${env.NODE_NAME}" - // on Windows, need a short path or we hit 256 character limit for paths - // using EXECUTOR_NUMBER guarantees that concurrent builds on same agent - // will not trample each other plus workaround for JENKINS-52657 - dir(isUnix() ? 'test' : "c:\\mvn-it-${EXECUTOR_NUMBER}.tmp") { - def WORK_DIR=pwd() - checkout tests - if (isUnix()) { - sh "rm -rvf $WORK_DIR/apache-maven-dist.zip $WORK_DIR/it-local-repo" - } else { - bat "if exist it-local-repo rmdir /s /q it-local-repo" - bat "if exist apache-maven-dist.zip del /q apache-maven-dist.zip" - } - unstash 'dist' - try { - withMaven(jdk: jdkName, maven: mvnName, mavenLocalRepo:"${WORK_DIR}/it-local-repo", options:[ - junitPublisher(ignoreAttachments: false) - ]) { - String cmd = "${runITscommand} -DmavenDistro=$WORK_DIR/apache-maven-dist.zip -Dmaven.test.failure.ignore=true" - if (stageId.endsWith('-jdk7')) { - // Java 7u80 has TLS 1.2 disabled by default: need to explicitly enable - cmd = "${cmd} -Dhttps.protocols=TLSv1.2" - } - - if (isUnix()) { - sh 'df -hT' - sh "${cmd}" - } else { - bat 'wmic logicaldisk get size,freespace,caption' - bat "${cmd}" - } - } - } finally { - archiveDirs(stageId, ['core-it-suite-logs':'core-it-suite/target/test-classes', - 'core-it-suite-reports':'core-it-suite/target/surefire-reports']) - deleteDir() // clean up after ourselves to reduce disk space - } - } - } - } - } - } +boolean isDeployedBranch() { + return env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'maven-4.0.x' || env.BRANCH_NAME == 'maven-3.9.x' } -// run the parallel ITs -parallel(runITsTasks) - -// JENKINS-34376 seems to make it hard to detect the aborted builds -} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) { - echo "[FAILURE-002] FlowInterruptedException ${e}" - // this ambiguous condition means a user probably aborted - if (e.causes.size() == 0) { - currentBuild.result = "ABORTED" - } else { - currentBuild.result = "FAILURE" - } - throw e -} catch (hudson.AbortException e) { - echo "[FAILURE-003] AbortException ${e}" - // this ambiguous condition means during a shell step, user probably aborted - if (e.getMessage().contains('script returned exit code 143')) { - currentBuild.result = "ABORTED" - } else { - currentBuild.result = "FAILURE" +/** + * To other developers, if you are using this method above, please use the following syntax. + * By default this method does NOT execute ITs anymore, just "install". + * + * mavenBuild("", " " + * + * @param jdk the jdk tool name (in jenkins) to use for this build + * @param extraArgs extra command line args + */ +def mavenBuild(jdk, extraArgs) { + script { + try { + withEnv(["JAVA_HOME=${tool "$jdk"}", + "PATH+MAVEN=${tool "$jdk"}/bin:${tool "maven_3_latest"}/bin", + "MAVEN_OPTS=-Xms4G -Xmx4G -Djava.awt.headless=true"]) { + sh "mvn --errors --batch-mode --show-version org.apache.maven.plugins:maven-wrapper-plugin:3.3.2:wrapper -Dmaven=3.9.10" + sh "echo run Its" + sh "./mvnw -e -B -V install $extraArgs" + } } - throw e -} catch (InterruptedException e) { - echo "[FAILURE-004] ${e}" - currentBuild.result = "ABORTED" - throw e -} catch (Throwable e) { - echo "[FAILURE-001] ${e}" - currentBuild.result = "FAILURE" - throw e -} finally { - // notify completion - stage("Notifications") { - jenkinsNotify() - } -} - -def archiveDirs(stageId, archives) { - archives.each { archivePrefix, pathToContent -> - if (fileExists(pathToContent)) { - zip(zipFile: "${archivePrefix}-${stageId}.zip", dir: pathToContent, archive: true) - } + finally { + junit testResults: '**/target/test-results-surefire/*.xml', allowEmptyResults: true } + } } diff --git a/README.md b/README.md index 0c77dabf55ae..65390379828a 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,18 @@ Apache Maven ============ [![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)][license] -[![Maven Central](https://img.shields.io/maven-central/v/org.apache.maven/apache-maven.svg?label=Maven%20Central)](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.apache.maven%22%20AND%20a%3A%22apache-maven%22) -[![Jenkins Status](https://img.shields.io/jenkins/s/https/builds.apache.org/job/maven-box/job/maven/job/master.svg?style=flat-square)][build] -[![Jenkins tests](https://img.shields.io/jenkins/t/https/builds.apache.org/job/maven-box/job/maven/job/master.svg?style=flat-square)][test-results] +[![Maven Central](https://img.shields.io/maven-central/v/org.apache.maven/apache-maven.svg?label=Maven%20Central&versionPrefix=3.)](https://search.maven.org/artifact/org.apache.maven/apache-maven) +[![Maven Central](https://img.shields.io/maven-central/v/org.apache.maven/apache-maven.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.apache.maven/apache-maven) +[![Reproducible Builds](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/org/apache/maven/maven/badge.json)](https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/org/apache/maven/maven/README.md) +[![Jenkins Status](https://img.shields.io/jenkins/s/https/ci-maven.apache.org/job/Maven/job/maven-box/job/maven/job/master.svg?)][build] +[![Jenkins tests](https://img.shields.io/jenkins/t/https/ci-maven.apache.org/job/Maven/job/maven-box/job/maven/job/master.svg?)][test-results] Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information. -If you think you have found a bug, please file an issue in the [Maven Issue Tracker](https://issues.apache.org/jira/browse/MNG). +If you think you have found a bug, please file an issue in the [Maven Issue Tracker](https://github.com/apache/maven/issues). Documentation ------------- @@ -39,48 +41,44 @@ the [Maven User List][users-list]. Where can I get the latest release? ----------------------------------- -You can download release source from our [download page][maven-download]. +You can download the release source from our [download page][maven-download]. Contributing ------------ -If you are interested in the development of Maven, please consult the -documentation first and afterwards you are welcome to join the developers -mailing list to ask question or discuss new ideas / features / bugs etc. +If you are interested in the development of Maven, please consult the +documentation first and afterward you are welcome to join the developers +mailing list to ask questions or discuss new ideas/features/bugs etc. Take a look into the [contribution guidelines](CONTRIBUTING.md). License ------- -This code is under the [Apache Licence v2][license] +This code is under the [Apache License, Version 2.0, January 2004][license]. -See the `NOTICE` file for required notices and attributions. +See the [`NOTICE`](./NOTICE) file for required notices and attributions. Donations --------- -You like Apache Maven? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development. - -License -------- -[Apache License, Version 2.0, January 2004][license] +Do you like Apache Maven? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development. Quick Build ------- If you want to bootstrap Maven, you'll need: -- Java 1.7+ -- Maven 3.0.5 or later +- Java 17+ +- Maven 3.6.3 or later - Run Maven, specifying a location into which the completed Maven distro should be installed: -``` -mvn -DdistributionTargetDir="$HOME/app/maven/apache-maven-3.6.x-SNAPSHOT" clean package -``` + ``` + mvn -DdistributionTargetDir="$HOME/app/maven/apache-maven-4.0.x-SNAPSHOT" clean package + ``` [home]: https://maven.apache.org/ [license]: https://www.apache.org/licenses/LICENSE-2.0 -[build]: https://builds.apache.org/job/maven-box/job/maven/job/master/ -[test-results]: https://builds.apache.org/job/maven-box/job/maven/job/master/lastCompletedBuild/testReport/ -[build-status]: https://img.shields.io/jenkins/s/https/builds.apache.org/job/maven-box/job/maven/job/master.svg?style=flat-square -[build-tests]: https://img.shields.io/jenkins/t/https/builds.apache.org/job/maven-box/job/maven/job/master.svg?style=flat-square +[build]: https://ci-maven.apache.org/job/Maven/job/maven-box/job/maven/job/master/ +[test-results]: https://ci-maven.apache.org/job/Maven/job/maven-box/job/maven/job/master/lastCompletedBuild/testReport/ +[build-status]: https://img.shields.io/jenkins/s/https/ci-maven.apache.org/job/Maven/job/maven-box/job/maven/job/master.svg? +[build-tests]: https://img.shields.io/jenkins/t/https/ci-maven.apache.org/job/Maven/job/maven-box/job/maven/job/master.svg? [maven-home]: https://maven.apache.org/ [maven-download]: https://maven.apache.org/download.cgi [users-list]: https://maven.apache.org/mailing-lists.html diff --git a/apache-maven/README.txt b/apache-maven/README.txt index 3e93a8433a53..19468f8fa090 100644 --- a/apache-maven/README.txt +++ b/apache-maven/README.txt @@ -16,50 +16,12 @@ Release Notes ------------- - The full list of changes can be found at https://maven.apache.org/docs/history.html. - - System Requirements - ------------------- - - JDK: - 1.7 or above (this is to execute Maven - it still allows you to build against 1.3 - and prior JDK's). - Memory: - No minimum requirement. - Disk: - Approximately 10MB is required for the Maven installation itself. In addition to - that, additional disk space will be used for your local Maven repository. The size - of your local repository will vary depending on usage but expect at least 500MB. - Operating System: - Windows: - Windows 2000 or above. - Unix based systems (Linux, Solaris and Mac OS X) and others: - No minimum requirement. + The full list of changes, system requirements and related can be found at https://maven.apache.org/docs/history.html. Installing Maven ---------------- - 1) Unpack the archive where you would like to store the binaries, e.g.: - - Unix-based operating systems (Linux, Solaris and Mac OS X) - tar zxvf apache-maven-3.x.y.tar.gz - Windows - unzip apache-maven-3.x.y.zip - - 2) A directory called "apache-maven-3.x.y" will be created. - - 3) Add the bin directory to your PATH, e.g.: - - Unix-based operating systems (Linux, Solaris and Mac OS X) - export PATH=/usr/local/apache-maven-3.x.y/bin:$PATH - Windows - set PATH="c:\program files\apache-maven-3.x.y\bin";%PATH% - - 4) Make sure JAVA_HOME is set to the location of your JDK - - 5) Run "mvn --version" to verify that it is correctly installed. - - For complete documentation, see https://maven.apache.org/download.html#Installation + For complete documentation see https://maven.apache.org/download.html#Installation Licensing --------- @@ -69,7 +31,7 @@ Maven URLS ---------- - Home Page: https://maven.apache.org/ + Home Page: https://maven.apache.org Downloads: https://maven.apache.org/download.html Release Notes: https://maven.apache.org/docs/history.html Mailing Lists: https://maven.apache.org/mailing-lists.html diff --git a/apache-maven/pom.xml b/apache-maven/pom.xml index 7d85e7525202..6257d12978db 100644 --- a/apache-maven/pom.xml +++ b/apache-maven/pom.xml @@ -1,5 +1,4 @@ - - 4.0.0 org.apache.maven maven - 3.6.3 + 4.1.0-SNAPSHOT apache-maven @@ -34,11 +32,11 @@ under the License. Apache Maven Distribution The Apache Maven distribution, source and binary, in zip and tar.gz formats. - - ${distributionId}-${project.version} - - + + org.apache.maven + maven-cli + org.apache.maven maven-embedder @@ -51,11 +49,7 @@ under the License. org.apache.maven maven-compat - - org.eclipse.sisu - org.eclipse.sisu.plexus - - + commons-cli commons-cli @@ -63,30 +57,13 @@ under the License. org.apache.maven.wagon wagon-http - shaded - - - org.apache.httpcomponents - httpclient - - - org.apache.httpcomponents - httpcore - - - org.apache.maven.wagon - wagon-http-shared - - - - org.jsoup - jsoup - runtime + org.apache.maven.wagon + wagon-file + + org.slf4j jcl-over-slf4j @@ -94,56 +71,103 @@ under the License. runtime - org.apache.maven.wagon - wagon-file + org.apache.maven.resolver + maven-resolver-connector-basic org.apache.maven.resolver - maven-resolver-connector-basic + maven-resolver-transport-file + org.apache.maven.resolver maven-resolver-transport-wagon + + + org.apache.maven.resolver + maven-resolver-transport-apache + + + + org.apache.maven.resolver + maven-resolver-transport-jdk + org.apache.maven - maven-slf4j-provider + maven-logging + + + org.jline + jline-reader - org.fusesource.jansi - jansi + org.jline + jline-terminal-jni + + + org.jline + jline-terminal-ffm + + + + + javax.inject + javax.inject + + + javax.annotation + javax.annotation-api + + + + + org.eclipse.sisu + org.eclipse.sisu.plexus + + + org.eclipse.sisu + org.eclipse.sisu.inject + + + com.google.inject + guice + classes + + + org.ow2.asm + asm + + + + false + + + true + + apache.snapshots + https://repository.apache.org/snapshots/ + + + - ${distributionFileName} - - - - org.apache.rat - apache-rat-plugin - - - src/bin/m2.conf - - - - - org.apache.maven.plugins maven-dependency-plugin - - jansi - META-INF/native/** - - unpack-jansi-native + unpack-jline-native unpack-dependencies + + jline-native + org/jline/nativ/** + @@ -163,12 +187,9 @@ under the License. org.apache.maven.plugins maven-surefire-plugin - - - basedir - ${basedir} - - + + ${basedir} + @@ -185,34 +206,60 @@ under the License. create-distro-packages - package single + package - src/main/assembly/bin.xml + src/assembly/bin.xml + + eu.maveniverse.maven.plugins + bom-builder3 + 1.2.1 + + + skinny-bom + + build-bom + + + skinny + Maven Dependencies Skinny BOM + Bill Of Materials for Apache Maven - Maven JARS only + maven-skinny-bom.xml + REACTOR + NONE + true + true + + + + fat-bom + + build-bom + + + fat + Maven Dependencies Fat BOM + Bill Of Materials for Apache Maven - All dependencies + maven-fat-bom.xml + REACTOR + CURRENT_PROJECT + true + true + + + + - - - apache.snapshots - http://repository.apache.org/snapshots/ - - true - - - false - - - - create-distribution-in-dir @@ -228,10 +275,10 @@ under the License. maven-clean-plugin + clean-target-dir clean - clean-target-dir prepare-package true @@ -249,17 +296,45 @@ under the License. create-distribution-dir - package single + package ./ false false ${distributionTargetDir} - src/main/assembly/dir.xml + src/assembly/dir.xml + + + + + + + + + + run-its + + + + maven-assembly-plugin + + + create-distribution-dir + + single + + package + + ./ + false + false + ${basedir}/target/maven + + src/assembly/dir.xml @@ -277,13 +352,13 @@ under the License. make-src-assembly - package single + package - src/main/assembly/src.xml + src/assembly/src.xml gnu @@ -294,31 +369,27 @@ under the License. net.nicoulaj.maven.plugins checksum-maven-plugin + 1.11 source-release-checksum - files + artifacts + + bin,src + - - - - ${project.build.directory} - - ${project.artifactId}-${project.version}-src.zip - ${project.artifactId}-${project.version}-src.tar.gz - ${project.artifactId}-${project.version}-bin.zip - ${project.artifactId}-${project.version}-bin.tar.gz - - - - true - + + versionlessMavenDist + + ${project.artifactId} + + diff --git a/apache-maven/src/main/assembly/bin.xml b/apache-maven/src/assembly/bin.xml similarity index 79% rename from apache-maven/src/main/assembly/bin.xml rename to apache-maven/src/assembly/bin.xml index a04557d506d0..da2c686efe8e 100644 --- a/apache-maven/src/main/assembly/bin.xml +++ b/apache-maven/src/assembly/bin.xml @@ -17,14 +17,14 @@ specific language governing permissions and limitations under the License. --> - + bin zip tar.gz - src/main/assembly/component.xml + src/assembly/component.xml diff --git a/apache-maven/src/main/assembly/component.xml b/apache-maven/src/assembly/component.xml similarity index 80% rename from apache-maven/src/main/assembly/component.xml rename to apache-maven/src/assembly/component.xml index fab2c55c5d5c..4d75c9a38ca8 100644 --- a/apache-maven/src/main/assembly/component.xml +++ b/apache-maven/src/assembly/component.xml @@ -16,8 +16,8 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - + false @@ -63,15 +63,7 @@ under the License. - target/dependency/META-INF/native - lib/jansi-native - - ** - - 0755 - - - src/bin + src/assembly/maven/bin bin *.cmd @@ -80,11 +72,24 @@ under the License. dos - src/bin + target/dependency/org/jline/nativ + lib/jline-native + + **/*.so + **/*.jnilib + **/*.dll + + + + src/assembly/maven/bin bin mvn + mvnenc + mvnsh + mvnup mvnDebug + mvnencDebug mvnyjp @@ -92,11 +97,11 @@ under the License. 0755 - src/conf + src/assembly/maven/conf conf - src/lib + src/assembly/maven/lib lib diff --git a/apache-maven/src/main/assembly/dir.xml b/apache-maven/src/assembly/dir.xml similarity index 79% rename from apache-maven/src/main/assembly/dir.xml rename to apache-maven/src/assembly/dir.xml index 580b2098d632..514cb9e89b7c 100644 --- a/apache-maven/src/main/assembly/dir.xml +++ b/apache-maven/src/assembly/dir.xml @@ -17,14 +17,14 @@ specific language governing permissions and limitations under the License. --> - + dir dir false - src/main/assembly/component.xml + src/assembly/component.xml diff --git a/apache-maven/src/bin/.gitattributes b/apache-maven/src/assembly/maven/bin/.gitattributes similarity index 100% rename from apache-maven/src/bin/.gitattributes rename to apache-maven/src/assembly/maven/bin/.gitattributes diff --git a/apache-maven/src/assembly/maven/bin/m2.conf b/apache-maven/src/assembly/maven/bin/m2.conf new file mode 100644 index 000000000000..b91431dea5f9 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/m2.conf @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +set maven.mainClass default org.apache.maven.cling.MavenCling + +main is ${maven.mainClass} from plexus.core + +set maven.conf default ${maven.home}/conf +set maven.installation.conf default ${maven.conf} + +[plexus.core] +load ${maven.conf}/logging +optionally ${maven.home}/lib/ext/redisson/*.jar +optionally ${maven.home}/lib/ext/hazelcast/*.jar +optionally ${user.home}/.m2/ext/*.jar +optionally ${maven.home}/lib/ext/*.jar +load ${maven.home}/lib/maven-*.jar +load ${maven.home}/lib/*.jar diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn new file mode 100755 index 000000000000..8559d47af557 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -0,0 +1,263 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# ----------------------------------------------------------------------------- +# Apache Maven Startup Script +# +# Environment Variable Prerequisites +# +# JAVA_HOME (Optional) Points to a Java installation. +# MAVEN_ARGS (Optional) Arguments passed to Maven before CLI arguments. +# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +# MAVEN_DEBUG_OPTS (Optional) Specify the debug options to use. Default value is "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${MAVEN_DEBUG_ADDRESS}" when --debug is used +# MAVEN_DEBUG_ADDRESS (Optional) Set the debug address. Default value is "localhost:8000" +# ----------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +mingw=false; +case "`uname`" in + CYGWIN*) cygwin=true;; + MINGW*) mingw=true;; +esac + +## resolve links - $0 may be a link to Maven's home +PRG="$0" + +# need this for relative symlinks +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi +done + +saveddir=`pwd` + +MAVEN_HOME=`dirname "$PRG"`/.. + +# make it fully qualified +MAVEN_HOME=`cd "$MAVEN_HOME" && pwd` + +cd "$saveddir" + +CLASSWORLDS_CONF="$MAVEN_HOME/bin/m2.conf" + +# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched +if $cygwin || $mingw ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + + if [ ! -x "$JAVACMD" ] ; then + echo "The JAVA_HOME environment variable is not defined correctly, so Apache Maven cannot be started." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" does not exist." >&2 + exit 1 + fi + fi +else + JAVACMD="`\\unset -f command; \\command -v java`" + + if [ ! -x "$JAVACMD" ] ; then + echo "The java(1) command does not exist in PATH nor is JAVA_HOME set, so Apache Maven cannot be started." >&2 + exit 1 + fi +fi + +if ! "$JAVACMD" --enable-native-access=ALL-UNNAMED -version >/dev/null 2>&1; then + echo "Error: Apache Maven 4.x requires Java 17 or newer to run." >&2 + "$JAVACMD" -version >&2 + echo "Please upgrade your Java installation or set JAVA_HOME to point to a compatible JDK." >&2 + exit 1 +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { +( + basedir=`find_file_argument_basedir "$@"` + wdir="$basedir" + while : + do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + if [ "$wdir" = '/' ] ; then + break + fi + wdir=`cd "$wdir/.."; pwd` + done + echo "$basedir" +) +} + +find_file_argument_basedir() { +( + basedir=`pwd` + + found_file_switch=0 + for arg in "$@"; do + if [ ${found_file_switch} -eq 1 ]; then + if [ -d "${arg}" ]; then + basedir=`cd "${arg}" && pwd -P` + elif [ -f "${arg}" ]; then + basedir=`dirname "${arg}"` + basedir=`cd "$basedir" && pwd -P` + if [ ! -d "$basedir" ]; then + echo "Directory $basedir extracted from the -f/--file command-line argument ${arg} does not exist" >&2 + exit 1 + fi + else + echo "POM file ${arg} specified with the -f/--file command line argument does not exist" >&2 + exit 1 + fi + break + fi + if [ "$arg" = "-f" -o "$arg" = "--file" ]; then + found_file_switch=1 + fi + done + echo "$basedir" +) +} + +# concatenates all lines of a file and replaces variables +concat_lines() { + if [ -f "$1" ]; then + # First convert all CR to LF using tr + tr '\r' '\n' < "$1" | \ + sed -e '/^$/d' -e 's/#.*$//' | \ + # Replace LF with NUL for xargs + tr '\n' '\0' | \ + # Split into words and process each argument + # Use -0 with NUL to avoid special behaviour on quotes + xargs -n 1 -0 | \ + while read -r arg; do + # Replace variables first + arg=$(echo "$arg" | sed \ + -e "s@\${MAVEN_PROJECTBASEDIR}@$MAVEN_PROJECTBASEDIR@g" \ + -e "s@\$MAVEN_PROJECTBASEDIR@$MAVEN_PROJECTBASEDIR@g") + + echo "$arg" + done | \ + tr '\n' ' ' + fi +} + +MAVEN_PROJECTBASEDIR="`find_maven_basedir "$@"`" +MAVEN_OPTS="$MAVEN_OPTS `concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config"`" +LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/plexus-classworlds-*.jar` +LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin and MinGW, switch paths to Windows format before running java(1) command +if $cygwin || $mingw ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --windows "$JAVA_HOME"` + LAUNCHER_JAR=`cygpath --windows "$LAUNCHER_JAR"` + CLASSWORLDS_CONF=`cygpath --windows "$CLASSWORLDS_CONF"` + MAVEN_HOME=`cygpath --windows "$MAVEN_HOME"` + MAVEN_PROJECTBASEDIR=`cygpath --windows "$MAVEN_PROJECTBASEDIR"` +fi + +handle_args() { + while [ $# -gt 0 ]; do + case $1 in + --debug) + if [ -z "$MAVEN_DEBUG_OPTS" ] ; then + MAVEN_DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${MAVEN_DEBUG_ADDRESS:-localhost:8000}" + else + echo "Ignoring --debug option as MAVEN_DEBUG_OPTS is already set" + fi + ;; + --yjp) + if [ ! -f "$YJPLIB" ]; then + echo "Error: Unable to autodetect the YJP library location. Please set YJPLIB variable" >&2 + exit 1 + fi + MAVEN_OPTS="-agentpath:$YJPLIB=onexit=snapshot,onexit=memory,tracing,onlylocal $MAVEN_OPTS" + ;; + --enc) + MAVEN_MAIN_CLASS="org.apache.maven.cling.MavenEncCling" + ;; + --shell) + MAVEN_MAIN_CLASS="org.apache.maven.cling.MavenShellCling" + ;; + --up) + MAVEN_MAIN_CLASS="org.apache.maven.cling.MavenUpCling" + ;; + *) + ;; + esac + shift + done +} + +handle_args "$@" +MAVEN_MAIN_CLASS=${MAVEN_MAIN_CLASS:=org.apache.maven.cling.MavenCling} + +cmd="\"$JAVACMD\" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + --enable-native-access=ALL-UNNAMED \ + -classpath \"$LAUNCHER_JAR\" \ + \"-Dclassworlds.conf=$CLASSWORLDS_CONF\" \ + \"-Dmaven.home=$MAVEN_HOME\" \ + \"-Dmaven.mainClass=$MAVEN_MAIN_CLASS\" \ + \"-Dlibrary.jline.path=${MAVEN_HOME}/lib/jline-native\" \ + \"-Dmaven.multiModuleProjectDirectory=$MAVEN_PROJECTBASEDIR\" \ + $LAUNCHER_CLASS \ + $MAVEN_ARGS" +# Add remaining arguments with proper quoting +for arg in "$@"; do + cmd="$cmd \"$arg\"" +done + +# Debug: print the command that will be executed +#echo "About to execute:" +#echo "$cmd" + +eval exec "$cmd" diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd new file mode 100644 index 000000000000..a3e8600df3d1 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvn.cmd @@ -0,0 +1,289 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. + +@REM ----------------------------------------------------------------------------- +@REM Apache Maven Startup Script +@REM +@REM Environment Variable Prerequisites +@REM +@REM JAVA_HOME (Optional) Points to a Java installation. +@REM MAVEN_ARGS (Optional) Arguments passed to Maven before CLI arguments. +@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands. +@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending. +@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +@REM ----------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO% + +@REM Clear/define a variable for any options to be inserted via script +@REM We want to avoid trying to parse the external MAVEN_OPTS variable +SET INTERNAL_MAVEN_OPTS= + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%"=="" goto skipRc +if exist "%PROGRAMDATA%\mavenrc.cmd" call "%PROGRAMDATA%\mavenrc.cmd" %* +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" echo Warning: The mavenrc_pre.bat script is deprecated and will be removed in a future version. >&2 +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" echo Warning: The mavenrc_pre.cmd script is deprecated and will be removed in a future version. >&2 +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +if exist "%USERPROFILE%\mavenrc.cmd" call "%USERPROFILE%\mavenrc.cmd" %* +:skipRc + +@setlocal + +set ERROR_CODE=0 + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%"=="" goto javaHomeSet +for %%i in (java.exe) do set "JAVACMD=%%~$PATH:i" +goto checkJavaCmd + +:javaHomeSet +set "JAVACMD=%JAVA_HOME%\bin\java.exe" + +if not exist "%JAVACMD%" ( + echo The JAVA_HOME environment variable is not defined correctly, so Apache Maven cannot be started. >&2 + echo JAVA_HOME is set to "%JAVA_HOME%", but "%%JAVA_HOME%%\bin\java.exe" does not exist. >&2 + goto error +) + +:checkJavaCmd +if not exist "%JAVACMD%" ( + echo The java.exe command does not exist in PATH nor is JAVA_HOME set, so Apache Maven cannot be started. >&2 + goto error +) + +@REM Check Java version by testing the Java 17+ flag +"%JAVACMD%" --enable-native-access=ALL-UNNAMED -version >nul 2>&1 +if ERRORLEVEL 1 ( + echo Error: Apache Maven 4.x requires Java 17 or newer to run. >&2 + "%JAVACMD%" -version >&2 + echo Please upgrade your Java installation or set JAVA_HOME to point to a compatible JDK. >&2 + goto error +) + +:chkMHome +set "MAVEN_HOME=%~dp0" +set "MAVEN_HOME=%MAVEN_HOME:~0,-5%" +if "%MAVEN_HOME%"=="" goto error + +:checkMCmd +if not exist "%MAVEN_HOME%\bin\mvn.cmd" goto error + +@REM ==== END VALIDATION ==== + +:init + +set "CLASSWORLDS_CONF=%MAVEN_HOME%\bin\m2.conf" + +@REM Find the project basedir, i.e., the directory that contains the directory ".mvn". +@REM Fallback to current working directory if not found. + +set "EXEC_DIR=%CD%" +set "WDIR=%EXEC_DIR%" + +@REM Look for the --file switch and start the search for the .mvn directory from the specified +@REM POM location, if supplied. + +set FILE_ARG= +:arg_loop +if "%~1" == "-f" ( + set "FILE_ARG=%~2" + shift + goto process_file_arg +) +if "%~1" == "--file" ( + set "FILE_ARG=%~2" + shift + goto process_file_arg +) +@REM If none of the above, skip the argument +shift +if not "%~1" == "" ( + goto arg_loop +) else ( + goto findBaseDir +) + +:process_file_arg +if "%FILE_ARG%" == "" ( + goto findBaseDir +) +if not exist "%FILE_ARG%" ( + echo POM file "%FILE_ARG%" specified the -f/--file command-line argument does not exist >&2 + goto error +) +if exist "%FILE_ARG%\*" ( + set "POM_DIR=%FILE_ARG%" +) else ( + call :get_directory_from_file "%FILE_ARG%" +) +if not exist "%POM_DIR%" ( + echo Directory "%POM_DIR%" extracted from the -f/--file command-line argument "%FILE_ARG%" does not exist >&2 + goto error +) +set "WDIR=%POM_DIR%" +goto findBaseDir + +:get_directory_from_file +set "POM_DIR=%~dp1" +:stripPomDir +if not "_%POM_DIR:~-1%"=="_\" goto pomDirStripped +set "POM_DIR=%POM_DIR:~0,-1%" +goto stripPomDir +:pomDirStripped +exit /b + +:findBaseDir +cd /d "%WDIR%" +set "WDIR=%CD%" +:findBaseDirLoop +if exist ".mvn" goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set "WDIR=%CD%" +goto findBaseDirLoop + +:baseDirFound +set "MAVEN_PROJECTBASEDIR=%WDIR%" +cd /d "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +if "_%EXEC_DIR:~-1%"=="_\" set "EXEC_DIR=%EXEC_DIR:~0,-1%" +set "MAVEN_PROJECTBASEDIR=%EXEC_DIR%" +cd /d "%EXEC_DIR%" + +:endDetectBaseDir + +if not exist "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadJvmConfig + +@setlocal EnableExtensions EnableDelayedExpansion +set JVM_CONFIG_MAVEN_OPTS= +for /F "usebackq tokens=* delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do ( + set "line=%%a" + + rem Skip empty lines and full-line comments + echo !line! | findstr /b /r /c:"[ ]*#" >nul + if errorlevel 1 ( + rem Handle end-of-line comments by taking everything before # + for /f "tokens=1* delims=#" %%i in ("!line!") do set "line=%%i" + + rem Trim leading/trailing spaces while preserving spaces in quotes + set "trimmed=!line!" + for /f "tokens=* delims= " %%i in ("!trimmed!") do set "trimmed=%%i" + for /l %%i in (1,1,100) do if "!trimmed:~-1!"==" " set "trimmed=!trimmed:~0,-1!" + + rem Replace MAVEN_PROJECTBASEDIR placeholders + set "trimmed=!trimmed:${MAVEN_PROJECTBASEDIR}=%MAVEN_PROJECTBASEDIR%!" + set "trimmed=!trimmed:$MAVEN_PROJECTBASEDIR=%MAVEN_PROJECTBASEDIR%!" + + if not "!trimmed!"=="" ( + if "!JVM_CONFIG_MAVEN_OPTS!"=="" ( + set "JVM_CONFIG_MAVEN_OPTS=!trimmed!" + ) else ( + set "JVM_CONFIG_MAVEN_OPTS=!JVM_CONFIG_MAVEN_OPTS! !trimmed!" + ) + ) + ) +) +@endlocal & set JVM_CONFIG_MAVEN_OPTS=%JVM_CONFIG_MAVEN_OPTS% + +:endReadJvmConfig + +@REM do not let MAVEN_PROJECTBASEDIR end with a single backslash which would escape the double quote. This happens when .mvn at drive root. +if "_%MAVEN_PROJECTBASEDIR:~-1%"=="_\" set "MAVEN_PROJECTBASEDIR=%MAVEN_PROJECTBASEDIR%\" + +if "%MAVEN_DEBUG_ADDRESS%"=="" set MAVEN_DEBUG_ADDRESS=localhost:8000 + +goto endHandleArgs +:handleArgs +if "%~1"=="--debug" ( + if "%MAVEN_DEBUG_OPTS%"=="" ( + set "MAVEN_DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%MAVEN_DEBUG_ADDRESS%" + ) +) else if "%~1"=="--yjp" ( + if not exist "%YJPLIB%" ( + echo Error: Unable to autodetect the YJP library location. Please set YJPLIB variable >&2 + exit /b 1 + ) + set "INTERNAL_MAVEN_OPTS=-agentpath:%YJPLIB%=onexit=snapshot,onexit=memory,tracing,onlylocal %INTERNAL_MAVEN_OPTS%" +) else if "%~1"=="--enc" ( + set "MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenEncCling" +) else if "%~1"=="--shell" ( + set "MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenShellCling" +) else if "%~1"=="--up" ( + set "MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenUpCling" +) +exit /b 0 + +:processArgs +if "%~1"=="" exit /b 0 +call :handleArgs %1 +shift +goto processArgs + +:endHandleArgs +call :processArgs %* + +for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set LAUNCHER_JAR="%%i" +set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher +if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling + +"%JAVACMD%" ^ + %INTERNAL_MAVEN_OPTS% ^ + %MAVEN_OPTS% ^ + %JVM_CONFIG_MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + --enable-native-access=ALL-UNNAMED ^ + -classpath %LAUNCHER_JAR% ^ + "-Dclassworlds.conf=%CLASSWORLDS_CONF%" ^ + "-Dmaven.home=%MAVEN_HOME%" ^ + "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" ^ + "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %LAUNCHER_CLASS% ^ + %MAVEN_ARGS% ^ + %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" echo Warning: The mavenrc_post.bat script is deprecated and will be removed in a future version. >&2 +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" echo Warning: The mavenrc_post.cmd script is deprecated and will be removed in a future version. >&2 +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +exit /b %ERROR_CODE% diff --git a/apache-maven/src/assembly/maven/bin/mvnDebug b/apache-maven/src/assembly/maven/bin/mvnDebug new file mode 100644 index 000000000000..1b4bd5a97ea9 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnDebug @@ -0,0 +1,32 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# ----------------------------------------------------------------------------- +# Apache Maven Debug Script +# +# Environment Variable Prerequisites +# +# JAVA_HOME (Optional) Points to a Java installation. +# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +# MAVEN_DEBUG_ADDRESS (Optional) Set the debug address. Default value is localhost:8000 +# ----------------------------------------------------------------------------- + +echo "This script is deprecated for removal, please use 'mvn --debug' instead" +"`dirname "$0"`/mvn" --debug "$@" diff --git a/apache-maven/src/assembly/maven/bin/mvnDebug.cmd b/apache-maven/src/assembly/maven/bin/mvnDebug.cmd new file mode 100644 index 000000000000..52439c366a42 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnDebug.cmd @@ -0,0 +1,41 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. + +@REM ----------------------------------------------------------------------------- +@REM Apache Maven Debug Script +@REM +@REM Environment Variable Prerequisites +@REM +@REM JAVA_HOME (Optional) Points to a Java installation. +@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands. +@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending. +@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +@REM MAVEN_DEBUG_ADDRESS (Optional) Set the debug address. Default value is localhost:8000 +@REM ----------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO% + +@setlocal + +echo "This script is deprecated for removal, please use 'mvn --debug' instead" +@call "%~dp0"mvn.cmd --debug %* diff --git a/apache-maven/src/assembly/maven/bin/mvnenc b/apache-maven/src/assembly/maven/bin/mvnenc new file mode 100644 index 000000000000..c6067dda62b2 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnenc @@ -0,0 +1,30 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# ----------------------------------------------------------------------------- +# Apache Maven Encrypt Script +# +# Environment Variable Prerequisites +# +# JAVA_HOME (Optional) Points to a Java installation. +# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +# ----------------------------------------------------------------------------- + +"`dirname "$0"`/mvn" --enc "$@" diff --git a/apache-maven/src/assembly/maven/bin/mvnenc.cmd b/apache-maven/src/assembly/maven/bin/mvnenc.cmd new file mode 100644 index 000000000000..f8b0fb9bdb65 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnenc.cmd @@ -0,0 +1,39 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. + +@REM ----------------------------------------------------------------------------- +@REM Apache Maven Encrypt Script +@REM +@REM Environment Variable Prerequisites +@REM +@REM JAVA_HOME (Optional) Points to a Java installation. +@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands. +@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending. +@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +@REM ----------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO% + +@setlocal + +@call "%~dp0"mvn.cmd --enc %* diff --git a/apache-maven/src/assembly/maven/bin/mvnsh b/apache-maven/src/assembly/maven/bin/mvnsh new file mode 100644 index 000000000000..e3f847cdf5b0 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnsh @@ -0,0 +1,30 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# ----------------------------------------------------------------------------- +# Apache Maven Encrypt Script +# +# Environment Variable Prerequisites +# +# JAVA_HOME (Optional) Points to a Java installation. +# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +# ----------------------------------------------------------------------------- + +"`dirname "$0"`/mvn" --shell "$@" diff --git a/apache-maven/src/assembly/maven/bin/mvnsh.cmd b/apache-maven/src/assembly/maven/bin/mvnsh.cmd new file mode 100644 index 000000000000..1b8ac48f3de6 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnsh.cmd @@ -0,0 +1,39 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. + +@REM ----------------------------------------------------------------------------- +@REM Apache Maven Encrypt Script +@REM +@REM Environment Variable Prerequisites +@REM +@REM JAVA_HOME (Optional) Points to a Java installation. +@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands. +@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending. +@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +@REM ----------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO% + +@setlocal + +@call "%~dp0"mvn.cmd --shell %* diff --git a/apache-maven/src/assembly/maven/bin/mvnup b/apache-maven/src/assembly/maven/bin/mvnup new file mode 100755 index 000000000000..83cce8714e63 --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnup @@ -0,0 +1,30 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# ----------------------------------------------------------------------------- +# Apache Maven Upgrade Script +# +# Environment Variable Prerequisites +# +# JAVA_HOME (Optional) Points to a Java installation. +# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +# ----------------------------------------------------------------------------- + +"`dirname "$0"`/mvn" --up "$@" diff --git a/apache-maven/src/assembly/maven/bin/mvnup.cmd b/apache-maven/src/assembly/maven/bin/mvnup.cmd new file mode 100644 index 000000000000..21fa1fadc93e --- /dev/null +++ b/apache-maven/src/assembly/maven/bin/mvnup.cmd @@ -0,0 +1,37 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. + +@REM ----------------------------------------------------------------------------- +@REM Apache Maven Upgrade Script +@REM +@REM Environment Variable Prerequisites +@REM +@REM JAVA_HOME (Optional) Points to a Java installation. +@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. +@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. +@REM ----------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO% + +@setlocal + +@call "%~dp0"mvn.cmd --up %* diff --git a/apache-maven/src/bin/mvnyjp b/apache-maven/src/assembly/maven/bin/mvnyjp old mode 100755 new mode 100644 similarity index 83% rename from apache-maven/src/bin/mvnyjp rename to apache-maven/src/assembly/maven/bin/mvnyjp index a35eabf1f642..5e76db1c8c86 --- a/apache-maven/src/bin/mvnyjp +++ b/apache-maven/src/assembly/maven/bin/mvnyjp @@ -27,9 +27,5 @@ # MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. # ----------------------------------------------------------------------------- -if [ ! -f "$YJPLIB" ]; then - echo "Error: Unable to autodetect the YJP library location. Please set YJPLIB variable" >&2 - exit 1 -fi - -env MAVEN_OPTS="-agentpath:$YJPLIB=onexit=snapshot,onexit=memory,tracing,onlylocal $MAVEN_OPTS" "`dirname "$0"`/mvn" "$@" +echo "This script is deprecated for removal, please use 'mvn --yjp' instead" +"`dirname "$0"`/mvn" --yjp "$@" diff --git a/apache-maven/src/assembly/maven/conf/logging/maven.logger.properties b/apache-maven/src/assembly/maven/conf/logging/maven.logger.properties new file mode 100644 index 000000000000..18c71c687cfe --- /dev/null +++ b/apache-maven/src/assembly/maven/conf/logging/maven.logger.properties @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +maven.logger.defaultLogLevel=info +maven.logger.showDateTime=false +maven.logger.showThreadName=false +maven.logger.showLogName=false +maven.logger.logFile=System.out +maven.logger.cacheOutputStream=true +maven.logger.levelInBrackets=true +maven.logger.log.Sisu=info +maven.logger.warnLevelString=WARNING + +# MNG-6181: mvn -X also prints all debug logging from HttpClient +maven.logger.log.org.apache.http=off +maven.logger.log.org.apache.http.wire=off diff --git a/apache-maven/src/assembly/maven/conf/maven-system.properties b/apache-maven/src/assembly/maven/conf/maven-system.properties new file mode 100644 index 000000000000..49466972a817 --- /dev/null +++ b/apache-maven/src/assembly/maven/conf/maven-system.properties @@ -0,0 +1,67 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# + +# +# Maven system properties +# +# The properties defined in this file will be made available through +# system properties at the very beginning of Maven's boot process. +# + +maven.installation.conf = ${maven.home}/conf +maven.user.conf = ${user.home}/.m2 +maven.project.conf = ${session.rootDirectory}/.mvn + +# Comma-separated list of files to include. +# Each item may be enclosed in quotes to gracefully include spaces. Items are trimmed before being loaded. +# If the first character of an item is a question mark, the load will silently fail if the file does not exist. +${includes} = ?"${maven.user.conf}/maven-system.properties", \ + ?"${maven.project.conf}/maven-system.properties" + +# +# Settings +# +# Define the default three levels for settings. +# The '-is' flag will override the 'maven.installation.settings' property. +# The '-ps' flag will override the 'maven.project.settings' property. +# The '-s' flag will override the 'maven.user.settings' property. +maven.installation.settings = ${maven.installation.conf}/settings.xml +maven.project.settings = ${maven.project.conf}/settings.xml +maven.user.settings = ${maven.user.conf}/settings.xml + +# +# Toolchains +# +# Define the default three levels for toolchains. +# The '-it' flag will override the 'maven.installation.toolchains' property. +# The '-t' flag will override the 'maven.user.toolchains' property. +maven.installation.toolchains = ${maven.installation.conf}/toolchains.xml +maven.user.toolchains = ${maven.user.conf}/toolchains.xml + +# +# Extensions +# +maven.installation.extensions = ${maven.installation.conf}/extensions.xml +maven.project.extensions = ${maven.project.conf}/extensions.xml +maven.user.extensions = ${maven.user.conf}/extensions.xml + +# +# Maven central repository URL. +# +maven.repo.central = ${env.MAVEN_REPO_CENTRAL:-https://repo.maven.apache.org/maven2} diff --git a/apache-maven/src/assembly/maven/conf/maven-user.properties b/apache-maven/src/assembly/maven/conf/maven-user.properties new file mode 100644 index 000000000000..a5813a5eac26 --- /dev/null +++ b/apache-maven/src/assembly/maven/conf/maven-user.properties @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# + +# +# Maven user properties +# +# The properties defined in this file will be made available through +# user properties at the very beginning of Maven's boot process. +# + +# Comma-separated list of files to include. +# Each item may be enclosed in quotes to gracefully include spaces. Items are trimmed before being loaded. +# If the first character of an item is a question mark, the load will silently fail if the file does not exist. +${includes} = ?"${maven.user.conf}/maven-user.properties", \ + ?"${maven.project.conf}/maven-user.properties" diff --git a/apache-maven/src/assembly/maven/conf/settings.xml b/apache-maven/src/assembly/maven/conf/settings.xml new file mode 100644 index 000000000000..8082c649c136 --- /dev/null +++ b/apache-maven/src/assembly/maven/conf/settings.xml @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + maven-default-http-blocker + external:http:* + Pseudo repository to mirror external repositories initially using HTTP. + http://0.0.0.0/ + true + + + + + + + central + Maven Central Repository + ${maven.repo.central} + + false + + + + + + + + central + Maven Central Repository + ${maven.repo.central} + + false + + + + + + + + + + + + + + diff --git a/apache-maven/src/assembly/maven/conf/toolchains.xml b/apache-maven/src/assembly/maven/conf/toolchains.xml new file mode 100644 index 000000000000..d8aa088b6a9d --- /dev/null +++ b/apache-maven/src/assembly/maven/conf/toolchains.xml @@ -0,0 +1,104 @@ + + + + + + + + + + \ No newline at end of file diff --git a/apache-maven/src/assembly/maven/lib/ext/README.txt b/apache-maven/src/assembly/maven/lib/ext/README.txt new file mode 100644 index 000000000000..ab7f12a71284 --- /dev/null +++ b/apache-maven/src/assembly/maven/lib/ext/README.txt @@ -0,0 +1,2 @@ +Use this directory to add third party extensions to Maven Core. These extensions can either extend or override +Maven's default implementation. diff --git a/apache-maven/src/assembly/maven/lib/ext/hazelcast/README.txt b/apache-maven/src/assembly/maven/lib/ext/hazelcast/README.txt new file mode 100644 index 000000000000..77d19b140de6 --- /dev/null +++ b/apache-maven/src/assembly/maven/lib/ext/hazelcast/README.txt @@ -0,0 +1,6 @@ +This directory is intended to contain Hazelcast [1] JARs for Maven Resolver Named Locks using Hazelcast. + +See here [2] on how to add necessary JARs. + +[1] https://github.com/hazelcast/hazelcast +[2] https://maven.apache.org/resolver/maven-resolver-named-locks-hazelcast/index.html#installation-testing diff --git a/apache-maven/src/assembly/maven/lib/ext/redisson/README.txt b/apache-maven/src/assembly/maven/lib/ext/redisson/README.txt new file mode 100644 index 000000000000..58342b19d142 --- /dev/null +++ b/apache-maven/src/assembly/maven/lib/ext/redisson/README.txt @@ -0,0 +1,6 @@ +This directory is intended to contain Redisson [1] JARs for Maven Resolver Named Locks using Redisson. + +See here [2] on how to add necessary JARs. + +[1] https://github.com/redisson/redisson +[2] https://maven.apache.org/resolver/maven-resolver-named-locks-redisson/index.html#installation-testing diff --git a/apache-maven/src/assembly/maven/lib/jline-native/README.txt b/apache-maven/src/assembly/maven/lib/jline-native/README.txt new file mode 100644 index 000000000000..ed74b45f7e13 --- /dev/null +++ b/apache-maven/src/assembly/maven/lib/jline-native/README.txt @@ -0,0 +1,8 @@ +This directory contains JLine native libraries extracted from JLine JAR. + +You can add your own build for platforms not natively supported by JLine. +See here [1] on how to compile for your platform and here [2] how libraries +follow JLine's directory and filename conventions. + +[1] https://github.com/jline/jline3/tree/master/native +[2] https://github.com/jline/jline3/blob/master/native/src/main/java/org/jline/nativ/OSInfo.java diff --git a/apache-maven/src/main/assembly/src.xml b/apache-maven/src/assembly/src.xml similarity index 94% rename from apache-maven/src/main/assembly/src.xml rename to apache-maven/src/assembly/src.xml index 9f43a0a7984e..940467421a87 100644 --- a/apache-maven/src/main/assembly/src.xml +++ b/apache-maven/src/assembly/src.xml @@ -17,8 +17,8 @@ specific language governing permissions and limitations under the License. --> - + src zip diff --git a/apache-maven/src/bin/m2.conf b/apache-maven/src/bin/m2.conf deleted file mode 100644 index 2235f820f5f0..000000000000 --- a/apache-maven/src/bin/m2.conf +++ /dev/null @@ -1,8 +0,0 @@ -main is org.apache.maven.cli.MavenCli from plexus.core - -set maven.conf default ${maven.home}/conf - -[plexus.core] -load ${maven.conf}/logging -optionally ${maven.home}/lib/ext/*.jar -load ${maven.home}/lib/*.jar diff --git a/apache-maven/src/bin/mvn b/apache-maven/src/bin/mvn deleted file mode 100755 index a554c661762d..000000000000 --- a/apache-maven/src/bin/mvn +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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 -# -# http://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. - -# ----------------------------------------------------------------------------- -# Apache Maven Startup Script -# -# Environment Variable Prerequisites -# -# JAVA_HOME Must point at your Java Development Kit installation. -# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. -# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. -# ----------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -mingw=false; -case "`uname`" in - CYGWIN*) cygwin=true;; - MINGW*) mingw=true;; -esac - -## resolve links - $0 may be a link to Maven's home -PRG="$0" - -# need this for relative symlinks -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi -done - -saveddir=`pwd` - -MAVEN_HOME=`dirname "$PRG"`/.. - -# make it fully qualified -MAVEN_HOME=`cd "$MAVEN_HOME" && pwd` - -cd "$saveddir" - -# For Cygwin, ensure paths are in Unix format before anything is touched -if $cygwin ; then - [ -n "$MAVEN_HOME" ] && - MAVEN_HOME=`cygpath --unix "$MAVEN_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For MinGW, ensure paths are in Unix format before anything is touched -if $mingw ; then - [ -n "$MAVEN_HOME" ] && - MAVEN_HOME=`(cd "$MAVEN_HOME"; pwd)` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`(cd "$JAVA_HOME"; pwd)` - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ] ; then - JAVACMD=`which java` -else - JAVACMD="$JAVA_HOME/bin/java" -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "The JAVA_HOME environment variable is not defined correctly" >&2 - echo "This environment variable is needed to run this program" >&2 - echo "NB: JAVA_HOME should point to a JDK not a JRE" >&2 - exit 1 -fi - -CLASSWORLDS_JAR=`echo "${MAVEN_HOME}"/boot/plexus-classworlds-*.jar` -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - [ -n "$MAVEN_HOME" ] && - MAVEN_HOME=`cygpath --path --windows "$MAVEN_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$CLASSWORLDS_JAR" ] && - CLASSWORLDS_JAR=`cygpath --path --windows "$CLASSWORLDS_JAR"` -fi - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { -( - basedir=`find_file_argument_basedir "$@"` - wdir="${basedir}" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - wdir=`cd "$wdir/.."; pwd` - done - echo "${basedir}" -) -} - -find_file_argument_basedir() { -( - basedir=`pwd` - - found_file_switch=0 - for arg in "$@"; do - if [ ${found_file_switch} -eq 1 ]; then - if [ -d "${arg}" ]; then - basedir=`cd "${arg}" && pwd -P` - elif [ -f "${arg}" ]; then - basedir=`dirname "${arg}"` - basedir=`cd "${basedir}" && pwd -P` - if [ ! -d "${basedir}" ]; then - echo "Directory ${basedir} extracted from the -f/--file command-line argument ${arg} does not exist" >&2 - exit 1 - fi - else - echo "POM file ${arg} specified with the -f/--file command line argument does not exist" >&2 - exit 1 - fi - break - fi - if [ "$arg" = "-f" -o "$arg" = "--file" ]; then - found_file_switch=1 - fi - done - echo "${basedir}" -) -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "`tr -s '\r\n' ' ' < "$1"`" - fi -} - -MAVEN_PROJECTBASEDIR="${MAVEN_BASEDIR:-`find_maven_basedir "$@"`}" -MAVEN_OPTS="`concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config"` $MAVEN_OPTS" - -# For Cygwin, switch project base directory path to Windows format before -# executing Maven otherwise this will cause Maven not to consider it. -if $cygwin ; then - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -export MAVEN_PROJECTBASEDIR - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "${CLASSWORLDS_JAR}" \ - "-Dclassworlds.conf=${MAVEN_HOME}/bin/m2.conf" \ - "-Dmaven.home=${MAVEN_HOME}" \ - "-Dlibrary.jansi.path=${MAVEN_HOME}/lib/jansi-native" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${CLASSWORLDS_LAUNCHER} "$@" diff --git a/apache-maven/src/bin/mvn.cmd b/apache-maven/src/bin/mvn.cmd deleted file mode 100644 index fd1b21e3f9bb..000000000000 --- a/apache-maven/src/bin/mvn.cmd +++ /dev/null @@ -1,205 +0,0 @@ -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. - -@REM ----------------------------------------------------------------------------- -@REM Apache Maven Startup Script -@REM -@REM Environment Variable Prerequisites -@REM -@REM JAVA_HOME Must point at your Java Development Kit installation. -@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands. -@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending. -@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. -@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. -@REM ----------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO% - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%"=="" goto OkJHome -for %%i in (java.exe) do set "JAVACMD=%%~$PATH:i" -goto checkJCmd - -:OkJHome -set "JAVACMD=%JAVA_HOME%\bin\java.exe" - -:checkJCmd -if exist "%JAVACMD%" goto chkMHome - -echo The JAVA_HOME environment variable is not defined correctly >&2 -echo This environment variable is needed to run this program >&2 -echo NB: JAVA_HOME should point to a JDK not a JRE >&2 -goto error - -:chkMHome -set "MAVEN_HOME=%~dp0.." -if not "%MAVEN_HOME%"=="" goto stripMHome -goto error - -:stripMHome -if not "_%MAVEN_HOME:~-1%"=="_\" goto checkMCmd -set "MAVEN_HOME=%MAVEN_HOME:~0,-1%" -goto stripMHome - -:checkMCmd -if exist "%MAVEN_HOME%\bin\mvn.cmd" goto init -goto error -@REM ==== END VALIDATION ==== - -:init - -set MAVEN_CMD_LINE_ARGS=%* - -@REM Find the project basedir, i.e., the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set "MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%" -if not "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set "EXEC_DIR=%CD%" -set "WDIR=%EXEC_DIR%" - -@REM Look for the --file switch and start the search for the .mvn directory from the specified -@REM POM location, if supplied. - -set FILE_ARG= -:arg_loop -if "%~1" == "-f" ( - set "FILE_ARG=%~2" - shift - goto process_file_arg -) -if "%~1" == "--file" ( - set "FILE_ARG=%~2" - shift - goto process_file_arg -) -@REM If none of the above, skip the argument -shift -if not "%~1" == "" ( - goto arg_loop -) else ( - goto findBaseDir -) - -:process_file_arg -if "%FILE_ARG%" == "" ( - goto findBaseDir -) -if not exist "%FILE_ARG%" ( - echo POM file "%FILE_ARG%" specified the -f/--file command-line argument does not exist >&2 - goto error -) -if exist "%FILE_ARG%\*" ( - set "POM_DIR=%FILE_ARG%" -) else ( - call :get_directory_from_file "%FILE_ARG%" -) -if not exist "%POM_DIR%" ( - echo Directory "%POM_DIR%" extracted from the -f/--file command-line argument "%FILE_ARG%" does not exist >&2 - goto error -) -set "WDIR=%POM_DIR%" -goto findBaseDir - -:get_directory_from_file -set "POM_DIR=%~dp1" -:stripPomDir -if not "_%POM_DIR:~-1%"=="_\" goto pomDirStripped -set "POM_DIR=%POM_DIR:~0,-1%" -goto stripPomDir -:pomDirStripped -exit /b - -:findBaseDir -cd /d "%WDIR%" -:findBaseDirLoop -if exist "%WDIR%\.mvn" goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set "WDIR=%CD%" -goto findBaseDirLoop - -:baseDirFound -set "MAVEN_PROJECTBASEDIR=%WDIR%" -cd /d "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -if "_%EXEC_DIR:~-1%"=="_\" set "EXEC_DIR=%EXEC_DIR:~0,-1%" -set "MAVEN_PROJECTBASEDIR=%EXEC_DIR%" -cd "%EXEC_DIR%" - -:endDetectBaseDir - -set "jvmConfig=\.mvn\jvm.config" -if not exist "%MAVEN_PROJECTBASEDIR%%jvmConfig%" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set CLASSWORLDS_JAR="%%i" -set CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -"%JAVACMD%" ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %CLASSWORLDS_JAR% ^ - "-Dclassworlds.conf=%MAVEN_HOME%\bin\m2.conf" ^ - "-Dmaven.home=%MAVEN_HOME%" ^ - "-Dlibrary.jansi.path=%MAVEN_HOME%\lib\jansi-native" ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %CLASSWORLDS_LAUNCHER% %MAVEN_CMD_LINE_ARGS% -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/apache-maven/src/bin/mvnDebug b/apache-maven/src/bin/mvnDebug deleted file mode 100755 index be495ff15d38..000000000000 --- a/apache-maven/src/bin/mvnDebug +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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 -# -# http://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. - -# ----------------------------------------------------------------------------- -# Apache Maven Debug Script -# -# Environment Variable Prerequisites -# -# JAVA_HOME Must point at your Java Development Kit installation. -# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. -# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. -# ----------------------------------------------------------------------------- - -MAVEN_DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000" - -echo Preparing to execute Maven in debug mode - -env MAVEN_OPTS="$MAVEN_OPTS" MAVEN_DEBUG_OPTS="$MAVEN_DEBUG_OPTS" "`dirname "$0"`/mvn" "$@" diff --git a/apache-maven/src/bin/mvnDebug.cmd b/apache-maven/src/bin/mvnDebug.cmd deleted file mode 100644 index 5f9a20a0267a..000000000000 --- a/apache-maven/src/bin/mvnDebug.cmd +++ /dev/null @@ -1,33 +0,0 @@ -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. - -@REM ----------------------------------------------------------------------------- -@REM Apache Maven Debug Script -@REM -@REM Environment Variable Prerequisites -@REM -@REM JAVA_HOME Must point at your Java Development Kit installation. -@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands. -@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending. -@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. -@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. -@REM ----------------------------------------------------------------------------- - -@setlocal -@set MAVEN_DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 - -@call "%~dp0"mvn.cmd %* diff --git a/apache-maven/src/conf/logging/simplelogger.properties b/apache-maven/src/conf/logging/simplelogger.properties deleted file mode 100644 index 64b331b4592c..000000000000 --- a/apache-maven/src/conf/logging/simplelogger.properties +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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 -# -# http://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. - -org.slf4j.simpleLogger.defaultLogLevel=info -org.slf4j.simpleLogger.showDateTime=false -org.slf4j.simpleLogger.showThreadName=false -org.slf4j.simpleLogger.showLogName=false -org.slf4j.simpleLogger.logFile=System.out -org.slf4j.simpleLogger.cacheOutputStream=true -org.slf4j.simpleLogger.levelInBrackets=true -org.slf4j.simpleLogger.log.Sisu=info -org.slf4j.simpleLogger.warnLevelString=WARNING - -# MNG-6181: mvn -X also prints all debug logging from HttpClient -# Be aware that the shaded packages are used -# org.apache.http -> org.apache.maven.wagon.providers.http.httpclient -org.slf4j.simpleLogger.log.org.apache.maven.wagon.providers.http.httpclient=off -org.slf4j.simpleLogger.log.org.apache.maven.wagon.providers.http.httpclient.wire=off diff --git a/apache-maven/src/conf/settings.xml b/apache-maven/src/conf/settings.xml deleted file mode 100644 index e27c579cf6db..000000000000 --- a/apache-maven/src/conf/settings.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apache-maven/src/conf/toolchains.xml b/apache-maven/src/conf/toolchains.xml deleted file mode 100644 index b2630723e7a4..000000000000 --- a/apache-maven/src/conf/toolchains.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/apache-maven/src/lib/ext/README.txt b/apache-maven/src/lib/ext/README.txt deleted file mode 100644 index 5ebe59eb394c..000000000000 --- a/apache-maven/src/lib/ext/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Use this directory to contribute 3rd-party extensions to the Maven core. These extensions can either extend or override -Maven's default implementation. diff --git a/apache-maven/src/lib/jansi-native/README.txt b/apache-maven/src/lib/jansi-native/README.txt deleted file mode 100644 index 22857a64efa4..000000000000 --- a/apache-maven/src/lib/jansi-native/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -This directory contains Jansi native libraries, extracted from Jansi jar. - -You can add your own extensions for platforms not natively supported by -Jansi: the libraries follow HawtJNI directory and filename conventions. -See http://fusesource.github.io/hawtjni/documentation/api/org/fusesource/hawtjni/runtime/Library.html - -See https://github.com/fusesource/jansi-native for native lib source. diff --git a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm index 25ac46f3770f..02ddf974cf45 100644 --- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm +++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm @@ -18,32 +18,43 @@ ## -Apache Maven includes a number of components and libraries with separate -copyright notices and license terms. Your use of those components are -subject to the terms and conditions of the following licenses: +Apache Maven includes a number of components and libraries with separate +copyright notices and license terms. Your use of those components are +subject to the terms and conditions of the following licenses: ## #set ( $apacheMavenGroupIds = [ "org.apache.maven", "org.apache.maven.wagon", "org.apache.maven.resolver", "org.apache.maven.shared" ] ) -#set ( $MITLicenseNames = [ "MIT License", "MIT license", "The MIT License" ] ) +#set ( $MITLicenseNames = [ "MIT License", "MIT license", "The MIT License", "MIT" ] ) #foreach ( $project in $projects ) -#**##foreach ( $license in $project.licenses) +#**##foreach ( $license in $project.licenses ) #* *##set ( $groupId = $project.artifact.groupId ) #* *##set ( $directory = 'lib' ) #* *##if ( !$apacheMavenGroupIds.contains( $groupId ) ) #* *### advertise about each non-Maven dependency #* *### -#* *### infer SPDX license code -#* *##if ( $license.name == "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0" ) -#* *##set ( $spdx = 'CDDL-1.0' ) -#* *##elseif ( $MITLicenseNames.contains( $license.name ) ) +#* *### infer SPDX license id +#* *##if ( $MITLicenseNames.contains( $license.name ) ) #* *##set ( $spdx = 'MIT' ) #* *##elseif ( $license.name == "Eclipse Public License, Version 1.0" ) #* *##set ( $spdx = 'EPL-1.0' ) -#* *##elseif ( $license.url.contains( "www.apache.org/licenses/LICENSE-2.0" ) ) -#* *##set ( $spdx = 'ASL-2.0' ) +#* *##elseif ( $license.name == "Eclipse Public License, Version 2.0" ) +#* *##set ( $spdx = 'EPL-2.0' ) +#* *##elseif ( $license.url.contains( "www.apache.org/licenses/LICENSE-2.0" ) + || $license.url.contains( "https://raw.github.com/hunterhacker/jdom/master/LICENSE.txt" ) ) +#* *##set ( $spdx = 'Apache-2.0' ) +#* *##elseif ( $license.name == "BSD-2-Clause" || $license.name == "The BSD 2-Clause License" + || $license.url.contains( "www.opensource.org/licenses/bsd-license" ) ) +#* *##set ( $spdx = 'BSD-2-Clause' ) +#* *##elseif ( $license.name == "BSD-3-Clause" + || $license.url.contains( "opensource.org/licenses/BSD-3-Clause" ) ) +#* *##set ( $spdx = 'BSD-3-Clause' ) +#* *##elseif ( $license.name == "Public Domain" ) +#* *##set ( $spdx = 'Public-Domain' ) +#* *##elseif ( $license.name == "CDDL + GPLv2 with classpath exception" ) +#* *##set ( $spdx = 'CDDL+GPLv2-with-classpath-exception' ) #* *##else #* *### unrecognized license will require analysis to know obligations -#* *##set ( $spdx = 'unrecognized' ) +#* *##set ( $spdx = $license ) #* *##end #* *### #* *### fix project urls that are wrong in pom @@ -62,19 +73,17 @@ subject to the terms and conditions of the following licenses: #* *### #* *### copy license file to lib/$artifactId.license #* *##set ( $licFile = $directory + '/' + $project.artifact.artifactId + '.license' ) -#* *##if ( $spdx == "MIT" ) -#* *### MIT license contains date and copyright that makes the file specific to each artifact -#* *##set ( $downloaded = $locator.getResourceAsFile( "licenses/${spdx}-${project.artifact.artifactId}-${project.artifact.version}.txt", "licenses/${licFile}" ) ) -#* *##else -#* *##set ( $downloaded = $locator.getResourceAsFile( "licenses/${spdx}.txt", "licenses/${licFile}" ) ) -#* *##end +#* *##set ( $downloaded = $locator.getResourceAsFile( "licenses/${spdx}.txt", "licenses/${licFile}" ) ) + #* *### add dependency info to output - $directory/${project.artifact.artifactId}-${project.artifact.version}.jar: $project.artifact.toString().replace( ':eclipse-plugin:', ':jar:' ) - $project.name + Project: $project.name #if ( $project.url )Project URL: ${project.url}#end - License: $license.name#if ( $spdx ) ($spdx)#end $license.url ($licFile) + License: $license.name#if ( $spdx ) ($spdx)#end + + License URL: $license.url ($licFile) #* *##end #**##end diff --git a/apache-maven/src/main/appended-resources/META-INF/NOTICE.vm b/apache-maven/src/main/appended-resources/META-INF/NOTICE.vm index acf7ba42e070..dabe9f3fe9d2 100644 --- a/apache-maven/src/main/appended-resources/META-INF/NOTICE.vm +++ b/apache-maven/src/main/appended-resources/META-INF/NOTICE.vm @@ -18,9 +18,9 @@ ## This software bundles the following NOTICE files from third party library providers: -META-INF/NOTICE in archive lib/guice-4.2.1-no_aop.jar +META-INF/NOTICE in archive lib/guice-5.1.0.jar Google Guice - Core Library -Copyright 2006-2018 Google, Inc. +Copyright 2006-2022 Google, Inc. This product includes software developed at The Apache Software Foundation (http://www.apache.org/). @@ -36,7 +36,7 @@ javolution (http://javolution.org/). This product includes software developed by Rome (https://rome.dev.java.net/). -about.html in archive lib/org.eclipse.sisu.inject-0.3.4.jar +about.html in archive lib/org.eclipse.sisu.inject-0.3.5.jar @@ -47,27 +47,27 @@ about.html in archive lib/org.eclipse.sisu.inject-0.3.4.jar

About org.eclipse.sisu.inject

- -

November 5, 2013

+ +

November 5, 2013

License

-

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the -Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available at http://www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" will mean the Content.

-

If you did not receive this Content directly from the Eclipse Foundation, the Content is +

If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may -apply to your use of any object code in the Content. Check the Redistributor's license that was +apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org.

Third Party Content

-

The Content includes items that have been sourced from third parties as set -out below. If you did not receive this Content directly from the Eclipse Foundation, -the following is provided for informational purposes only, and you should look +

The Content includes items that have been sourced from third parties as set +out below. If you did not receive this Content directly from the Eclipse Foundation, +the following is provided for informational purposes only, and you should look to the Redistributor's license for terms and conditions of use.

ASM 4.1

diff --git a/apache-maven/src/main/appended-resources/licenses/ASL-2.0.txt b/apache-maven/src/main/appended-resources/licenses/Apache-2.0.txt similarity index 100% rename from apache-maven/src/main/appended-resources/licenses/ASL-2.0.txt rename to apache-maven/src/main/appended-resources/licenses/Apache-2.0.txt diff --git a/apache-maven/src/main/appended-resources/licenses/BSD-2-Clause.txt b/apache-maven/src/main/appended-resources/licenses/BSD-2-Clause.txt new file mode 100644 index 000000000000..04e0d095685b --- /dev/null +++ b/apache-maven/src/main/appended-resources/licenses/BSD-2-Clause.txt @@ -0,0 +1,24 @@ +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apache-maven/src/main/appended-resources/licenses/BSD-3-Clause.txt b/apache-maven/src/main/appended-resources/licenses/BSD-3-Clause.txt new file mode 100644 index 000000000000..fdc6a97e5fd9 --- /dev/null +++ b/apache-maven/src/main/appended-resources/licenses/BSD-3-Clause.txt @@ -0,0 +1,28 @@ +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apache-maven/src/main/appended-resources/licenses/CDDL+GPLv2-with-classpath-exception.txt b/apache-maven/src/main/appended-resources/licenses/CDDL+GPLv2-with-classpath-exception.txt new file mode 100644 index 000000000000..b1c74f95ede8 --- /dev/null +++ b/apache-maven/src/main/appended-resources/licenses/CDDL+GPLv2-with-classpath-exception.txt @@ -0,0 +1,759 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and + the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software + or previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available + under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form + of computer software code that is originally released under this + License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, + this License. For legal entities, "You" includes any entity which + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, the Initial Developer + hereby grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Software (or portions thereof), with or without Modifications, + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and + offer for sale, and/or otherwise dispose of the Original Software + (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on + the date Initial Developer first distributes or otherwise makes the + Original Software available to a third party under the terms of this + License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original Software, or + (2) for infringements caused by: (i) the modification of the + Original Software, or (ii) the combination of the Original Software + with other software or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, each Contributor hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof), either on an + unmodified basis, with other Modifications, as Covered Software + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling + of Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective + on the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted from the + Contributor Version; (2) for infringements caused by: (i) third + party modifications of Contributor Version, or (ii) the combination + of Modifications made by that Contributor with other software + (except as part of the Contributor Version) or other devices; or (3) + under Patent Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available + in Executable form must also be made available in Source Code form + and that Source Code form must be distributed only under the terms + of this License. You must include a copy of this License with every + copy of the Source Code form of the Covered Software You distribute + or otherwise make available. You must inform recipients of any such + Covered Software in Executable form as to how they can obtain such + Covered Software in Source Code form in a reasonable manner on or + through a medium customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or You + have sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may not + remove or alter any copyright, patent or trademark notices contained + within the Covered Software, or any notices of licensing or any + descriptive text giving attribution to any Contributor or the + Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version of + this License or the recipients' rights hereunder. You may choose to + offer, and to charge a fee for, warranty, support, indemnity or + liability obligations to one or more recipients of Covered Software. + However, you may do so only on Your own behalf, and not on behalf of + the Initial Developer or any Contributor. You must make it + absolutely clear that any such warranty, support, indemnity or + liability obligation is offered by You alone, and You hereby agree + to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a + result of warranty, support, indemnity or liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under + the terms of this License or under the terms of a license of Your + choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the Covered + Software in Executable form under a different license, You must make + it absolutely clear that any terms which differ from this License + are offered by You alone, not by the Initial Developer or + Contributor. You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and distribute + the Larger Work as a single product. In such a case, You must make + sure the requirements of this License are fulfilled for the Covered + Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or + new versions of this License from time to time. Each version will be + given a distinguishing version number. Except as provided in Section + 4.3, no one other than the license steward has the right to modify + this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. If + the Initial Developer includes a notice in the Original Software + prohibiting it from being distributed or otherwise made available + under any subsequent version of the License, You must distribute and + make the Covered Software available under the terms of the version + of the License under which You originally received the Covered + Software. Otherwise, You may also choose to use, distribute or + otherwise make the Covered Software available under the terms of any + subsequent version of the License published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license and + remove any references to the name of the license steward (except to + note that the license differs from this License); and (b) otherwise + make it clear that the license contains terms which differ from this + License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR + NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE + DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY + OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, + REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN + ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS + AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond the + termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that the + Participant Software (meaning the Contributor Version where the + Participant is a Contributor or the Original Software where the + Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if the + Initial Developer is not the Participant) and all Contributors under + Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice + from Participant terminate prospectively and automatically at the + expiration of such 60 day notice period, unless if within such 60 + day period You withdraw Your claim with respect to the Participant + Software against such Participant either unilaterally or pursuant to + a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly + infringes any patent where such claim is resolved (such as by + license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE + TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER + FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR + LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE + POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT + APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH + PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH + LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined + in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § + 252.227-7014(a)(1)) and "commercial computer software documentation" + as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent + with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 + (June 1995), all U.S. Government End Users acquire Covered Software + with only those rights set forth herein. This U.S. Government Rights + clause is in lieu of, and supersedes, any other FAR, DFAR, or other + clause or provision that addresses Government rights in computer + software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + the law of the jurisdiction specified in a notice contained within + the Original Software (except to the extent applicable law, if any, + provides otherwise), excluding such jurisdiction's conflict-of-law + provisions. Any litigation relating to this License shall be subject + to the jurisdiction of the courts located in the jurisdiction and + venue specified in a notice contained within the Original Software, + with the losing party responsible for costs, including, without + limitation, court costs and reasonable attorneys' fees and expenses. + The application of the United Nations Convention on Contracts for + the International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall be + construed against the drafter shall not apply to this License. You + agree that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, distribute + or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +------------------------------------------------------------------------ + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor +Boston, MA 02110-1335 +USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +Certain source files distributed by Oracle America, Inc. and/or its +affiliates are subject to the following clarification and special +exception to the GPLv2, based on the GNU Project exception for its +Classpath libraries, known as the GNU Classpath Exception, but only +where Oracle has expressly included in the particular source file's +header the words "Oracle designates this particular file as subject to +the "Classpath" exception as provided by Oracle in the LICENSE file +that accompanied this code." + +You should also note that Oracle includes multiple, independent +programs in this software package. Some of those programs are provided +under licenses deemed incompatible with the GPLv2 by the Free Software +Foundation and others. For example, the package includes programs +licensed under the Apache License, Version 2.0. Such programs are +licensed to you under their original licenses. + +Oracle facilitates your further distribution of this package by adding +the Classpath Exception to the necessary parts of its GPLv2 code, which +permits you to use that code in combination with other independent +modules not licensed under the GPLv2. However, note that this would +not permit you to commingle code under an incompatible license with +Oracle's GPLv2 licensed code by, for example, cutting and pasting such +code into a file also containing Oracle's GPLv2 licensed code and then +distributing the result. Additionally, if you were to remove the +Classpath Exception from any of the files to which it applies and +distribute the result, you would likely be required to license some or +all of the other code in that distribution under the GPLv2 as well, and +since the GPLv2 is incompatible with the license terms of some items +included in the distribution by Oracle, removing the Classpath +Exception could therefore effectively compromise your ability to +further distribute the package. + +Proceed with caution and we recommend that you obtain the advice of a +lawyer skilled in open source matters before removing the Classpath +Exception or making modifications to this package which may +subsequently be redistributed and/or involve the use of third party +software. + +CLASSPATH EXCEPTION +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you may extend this +exception to your version of the library, but you are not obligated to +do so. If you do not wish to do so, delete this exception statement +from your version. diff --git a/apache-maven/src/main/appended-resources/licenses/CDDL-1.0.txt b/apache-maven/src/main/appended-resources/licenses/CDDL-1.0.txt deleted file mode 100644 index 9bc6342e22c9..000000000000 --- a/apache-maven/src/main/appended-resources/licenses/CDDL-1.0.txt +++ /dev/null @@ -1,384 +0,0 @@ -COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 - - - 1. Definitions. - - 1.1. "Contributor" means each individual or entity that - creates or contributes to the creation of Modifications. - - 1.2. "Contributor Version" means the combination of the - Original Software, prior Modifications used by a - Contributor (if any), and the Modifications made by that - particular Contributor. - - 1.3. "Covered Software" means (a) the Original Software, or - (b) Modifications, or (c) the combination of files - containing Original Software with files containing - Modifications, in each case including portions thereof. - - 1.4. "Executable" means the Covered Software in any form - other than Source Code. - - 1.5. "Initial Developer" means the individual or entity - that first makes Original Software available under this - License. - - 1.6. "Larger Work" means a work which combines Covered - Software or portions thereof with code not governed by the - terms of this License. - - 1.7. "License" means this document. - - 1.8. "Licensable" means having the right to grant, to the - maximum extent possible, whether at the time of the initial - grant or subsequently acquired, any and all of the rights - conveyed herein. - - 1.9. "Modifications" means the Source Code and Executable - form of any of the following: - - A. Any file that results from an addition to, - deletion from or modification of the contents of a - file containing Original Software or previous - Modifications; - - B. Any new file that contains any part of the - Original Software or previous Modification; or - - C. Any new file that is contributed or otherwise made - available under the terms of this License. - - 1.10. "Original Software" means the Source Code and - Executable form of computer software code that is - originally released under this License. - - 1.11. "Patent Claims" means any patent claim(s), now owned - or hereafter acquired, including without limitation, - method, process, and apparatus claims, in any patent - Licensable by grantor. - - 1.12. "Source Code" means (a) the common form of computer - software code in which modifications are made and (b) - associated documentation included in or with such code. - - 1.13. "You" (or "Your") means an individual or a legal - entity exercising rights under, and complying with all of - the terms of, this License. For legal entities, "You" - includes any entity which controls, is controlled by, or is - under common control with You. For purposes of this - definition, "control" means (a) the power, direct or - indirect, to cause the direction or management of such - entity, whether by contract or otherwise, or (b) ownership - of more than fifty percent (50%) of the outstanding shares - or beneficial ownership of such entity. - - 2. License Grants. - - 2.1. The Initial Developer Grant. - - Conditioned upon Your compliance with Section 3.1 below and - subject to third party intellectual property claims, the - Initial Developer hereby grants You a world-wide, - royalty-free, non-exclusive license: - - (a) under intellectual property rights (other than - patent or trademark) Licensable by Initial Developer, - to use, reproduce, modify, display, perform, - sublicense and distribute the Original Software (or - portions thereof), with or without Modifications, - and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, - using or selling of Original Software, to make, have - made, use, practice, sell, and offer for sale, and/or - otherwise dispose of the Original Software (or - portions thereof). - - (c) The licenses granted in Sections 2.1(a) and (b) - are effective on the date Initial Developer first - distributes or otherwise makes the Original Software - available to a third party under the terms of this - License. - - (d) Notwithstanding Section 2.1(b) above, no patent - license is granted: (1) for code that You delete from - the Original Software, or (2) for infringements - caused by: (i) the modification of the Original - Software, or (ii) the combination of the Original - Software with other software or devices. - - 2.2. Contributor Grant. - - Conditioned upon Your compliance with Section 3.1 below and - subject to third party intellectual property claims, each - Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - (a) under intellectual property rights (other than - patent or trademark) Licensable by Contributor to - use, reproduce, modify, display, perform, sublicense - and distribute the Modifications created by such - Contributor (or portions thereof), either on an - unmodified basis, with other Modifications, as - Covered Software and/or as part of a Larger Work; and - - - (b) under Patent Claims infringed by the making, - using, or selling of Modifications made by that - Contributor either alone and/or in combination with - its Contributor Version (or portions of such - combination), to make, use, sell, offer for sale, - have made, and/or otherwise dispose of: (1) - Modifications made by that Contributor (or portions - thereof); and (2) the combination of Modifications - made by that Contributor with its Contributor Version - (or portions of such combination). - - (c) The licenses granted in Sections 2.2(a) and - 2.2(b) are effective on the date Contributor first - distributes or otherwise makes the Modifications - available to a third party. - - (d) Notwithstanding Section 2.2(b) above, no patent - license is granted: (1) for any code that Contributor - has deleted from the Contributor Version; (2) for - infringements caused by: (i) third party - modifications of Contributor Version, or (ii) the - combination of Modifications made by that Contributor - with other software (except as part of the - Contributor Version) or other devices; or (3) under - Patent Claims infringed by Covered Software in the - absence of Modifications made by that Contributor. - - 3. Distribution Obligations. - - 3.1. Availability of Source Code. - - Any Covered Software that You distribute or otherwise make - available in Executable form must also be made available in - Source Code form and that Source Code form must be - distributed only under the terms of this License. You must - include a copy of this License with every copy of the - Source Code form of the Covered Software You distribute or - otherwise make available. You must inform recipients of any - such Covered Software in Executable form as to how they can - obtain such Covered Software in Source Code form in a - reasonable manner on or through a medium customarily used - for software exchange. - - 3.2. Modifications. - - The Modifications that You create or to which You - contribute are governed by the terms of this License. You - represent that You believe Your Modifications are Your - original creation(s) and/or You have sufficient rights to - grant the rights conveyed by this License. - - 3.3. Required Notices. - - You must include a notice in each of Your Modifications - that identifies You as the Contributor of the Modification. - You may not remove or alter any copyright, patent or - trademark notices contained within the Covered Software, or - any notices of licensing or any descriptive text giving - attribution to any Contributor or the Initial Developer. - - 3.4. Application of Additional Terms. - - You may not offer or impose any terms on any Covered - Software in Source Code form that alters or restricts the - applicable version of this License or the recipients" - rights hereunder. You may choose to offer, and to charge a - fee for, warranty, support, indemnity or liability - obligations to one or more recipients of Covered Software. - However, you may do so only on Your own behalf, and not on - behalf of the Initial Developer or any Contributor. You - must make it absolutely clear that any such warranty, - support, indemnity or liability obligation is offered by - You alone, and You hereby agree to indemnify the Initial - Developer and every Contributor for any liability incurred - by the Initial Developer or such Contributor as a result of - warranty, support, indemnity or liability terms You offer. - - - 3.5. Distribution of Executable Versions. - - You may distribute the Executable form of the Covered - Software under the terms of this License or under the terms - of a license of Your choice, which may contain terms - different from this License, provided that You are in - compliance with the terms of this License and that the - license for the Executable form does not attempt to limit - or alter the recipient"s rights in the Source Code form - from the rights set forth in this License. If You - distribute the Covered Software in Executable form under a - different license, You must make it absolutely clear that - any terms which differ from this License are offered by You - alone, not by the Initial Developer or Contributor. You - hereby agree to indemnify the Initial Developer and every - Contributor for any liability incurred by the Initial - Developer or such Contributor as a result of any such terms - You offer. - - 3.6. Larger Works. - - You may create a Larger Work by combining Covered Software - with other code not governed by the terms of this License - and distribute the Larger Work as a single product. In such - a case, You must make sure the requirements of this License - are fulfilled for the Covered Software. - - 4. Versions of the License. - - 4.1. New Versions. - - Sun Microsystems, Inc. is the initial license steward and - may publish revised and/or new versions of this License - from time to time. Each version will be given a - distinguishing version number. Except as provided in - Section 4.3, no one other than the license steward has the - right to modify this License. - - 4.2. Effect of New Versions. - - You may always continue to use, distribute or otherwise - make the Covered Software available under the terms of the - version of the License under which You originally received - the Covered Software. If the Initial Developer includes a - notice in the Original Software prohibiting it from being - distributed or otherwise made available under any - subsequent version of the License, You must distribute and - make the Covered Software available under the terms of the - version of the License under which You originally received - the Covered Software. Otherwise, You may also choose to - use, distribute or otherwise make the Covered Software - available under the terms of any subsequent version of the - License published by the license steward. - - 4.3. Modified Versions. - - When You are an Initial Developer and You want to create a - new license for Your Original Software, You may create and - use a modified version of this License if You: (a) rename - the license and remove any references to the name of the - license steward (except to note that the license differs - from this License); and (b) otherwise make it clear that - the license contains terms which differ from this License. - - - 5. DISCLAIMER OF WARRANTY. - - COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" - BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, - INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED - SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR - PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND - PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY - COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE - INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF - ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF - WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF - ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS - DISCLAIMER. - - 6. TERMINATION. - - 6.1. This License and the rights granted hereunder will - terminate automatically if You fail to comply with terms - herein and fail to cure such breach within 30 days of - becoming aware of the breach. Provisions which, by their - nature, must remain in effect beyond the termination of - this License shall survive. - - 6.2. If You assert a patent infringement claim (excluding - declaratory judgment actions) against Initial Developer or - a Contributor (the Initial Developer or Contributor against - whom You assert such claim is referred to as "Participant") - alleging that the Participant Software (meaning the - Contributor Version where the Participant is a Contributor - or the Original Software where the Participant is the - Initial Developer) directly or indirectly infringes any - patent, then any and all rights granted directly or - indirectly to You by such Participant, the Initial - Developer (if the Initial Developer is not the Participant) - and all Contributors under Sections 2.1 and/or 2.2 of this - License shall, upon 60 days notice from Participant - terminate prospectively and automatically at the expiration - of such 60 day notice period, unless if within such 60 day - period You withdraw Your claim with respect to the - Participant Software against such Participant either - unilaterally or pursuant to a written agreement with - Participant. - - 6.3. In the event of termination under Sections 6.1 or 6.2 - above, all end user licenses that have been validly granted - by You or any distributor hereunder prior to termination - (excluding licenses granted to You by any distributor) - shall survive termination. - - 7. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT - (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE - INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF - COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE - LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR - CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT - LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK - STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER - COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN - INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF - LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL - INJURY RESULTING FROM SUCH PARTY"S NEGLIGENCE TO THE EXTENT - APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO - NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR - CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT - APPLY TO YOU. - - 8. U.S. GOVERNMENT END USERS. - - The Covered Software is a "commercial item," as that term is - defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial - computer software" (as that term is defined at 48 C.F.R. " - 252.227-7014(a)(1)) and "commercial computer software - documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. - 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 - through 227.7202-4 (June 1995), all U.S. Government End Users - acquire Covered Software with only those rights set forth herein. - This U.S. Government Rights clause is in lieu of, and supersedes, - any other FAR, DFAR, or other clause or provision that addresses - Government rights in computer software under this License. - - 9. MISCELLANEOUS. - - This License represents the complete agreement concerning subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the - extent necessary to make it enforceable. This License shall be - governed by the law of the jurisdiction specified in a notice - contained within the Original Software (except to the extent - applicable law, if any, provides otherwise), excluding such - jurisdiction"s conflict-of-law provisions. Any litigation - relating to this License shall be subject to the jurisdiction of - the courts located in the jurisdiction and venue specified in a - notice contained within the Original Software, with the losing - party responsible for costs, including, without limitation, court - costs and reasonable attorneys" fees and expenses. The - application of the United Nations Convention on Contracts for the - International Sale of Goods is expressly excluded. Any law or - regulation which provides that the language of a contract shall - be construed against the drafter shall not apply to this License. - You agree that You alone are responsible for compliance with the - United States export administration regulations (and the export - control laws and regulation of any other countries) when You use, - distribute or otherwise make available any Covered Software. - - 10. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is - responsible for claims and damages arising, directly or - indirectly, out of its utilization of rights under this License - and You agree to work with Initial Developer and Contributors to - distribute such responsibility on an equitable basis. Nothing - herein is intended or shall be deemed to constitute any admission - of liability. diff --git a/apache-maven/src/main/appended-resources/licenses/EPL-2.0.txt b/apache-maven/src/main/appended-resources/licenses/EPL-2.0.txt new file mode 100644 index 000000000000..e55f34467e25 --- /dev/null +++ b/apache-maven/src/main/appended-resources/licenses/EPL-2.0.txt @@ -0,0 +1,277 @@ +Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + +"Contributor" means any person or entity that Distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone +or when combined with the Program. + +"Program" means the Contributions Distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement +or any Secondary License (as applicable), including Contributors. + +"Derivative Works" shall mean any work, whether in Source Code or other +form, that is based on (or derived from) the Program and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. + +"Modified Works" shall mean any work in Source Code or other form that +results from an addition to, deletion from, or modification of the +contents of the Program, including, for purposes of clarity any new file +in Source Code form that contains any contents of the Program. Modified +Works shall not include works that contain only declarations, +interfaces, types, classes, structures, or files of the Program solely +in each case in order to link to, bind by name, or subclass the Program +or Modified Works thereof. + +"Distribute" means the acts of a) distributing or b) making available +in any manner that enables the transfer of a copy. + +"Source Code" means the form of a Program preferred for making +modifications, including but not limited to software source code, +documentation source, and configuration files. + +"Secondary License" means either the GNU General Public License, +Version 2.0, or any later versions of that license, including any +exceptions or additional permissions as identified by the initial +Contributor. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + +3. REQUIREMENTS + +3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + +3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + +3.3 Contributors may not remove or alter any copyright, patent, +trademark, attribution notices, disclaimers of warranty, or limitations +of liability ("notices") contained within the Program from any copy of +the Program which they Distribute, provided that Contributors may add +their own appropriate notices. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, +the Contributor who includes the Program in a commercial product +offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes +the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and indemnify every +other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits +and other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the Program +in a commercial product offering. The obligations in this section do not +apply to any claims or Losses relating to any actual or alleged +intellectual property infringement. In order to qualify, an Indemnified +Contributor must: a) promptly notify the Commercial Contributor in +writing of such claim, and b) allow the Commercial Contributor to control, +and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those performance +claims and warranties, and if a court requires any other Contributor to +pay any damages as a result, the Commercial Contributor must pay +those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF +TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE. Each Recipient is solely responsible for determining the +appropriateness of using and distributing the Program and assumes all +risks associated with its exercise of rights under this Agreement, +including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs +or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS +SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software +or hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, +but in order to avoid inconsistency the Agreement is copyrighted and +may only be modified in the following manner. The Agreement Steward +reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement +Steward has the right to modify this Agreement. The Eclipse Foundation +is the initial Agreement Steward. The Eclipse Foundation may assign the +responsibility to serve as the Agreement Steward to a suitable separate +entity. Each new version of the Agreement will be given a distinguishing +version number. The Program (including Contributions) may always be +Distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to Distribute the Program (including its +Contributions) under the new version. + +Except as expressly stated in Sections 2(a) and 2(b) above, Recipient +receives no rights or licenses to the intellectual property of any +Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted +under this Agreement are reserved. Nothing in this Agreement is intended +to be enforceable by any entity that is not a Contributor or Recipient. +No third-party beneficiary rights are created under this Agreement. + +Exhibit A - Form of Secondary Licenses Notice + +"This Source Code may also be made available under the following +Secondary Licenses when the conditions for such availability set forth +in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), +version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. \ No newline at end of file diff --git a/apache-maven/src/main/appended-resources/licenses/MIT-jsoup-1.12.1.txt b/apache-maven/src/main/appended-resources/licenses/MIT-jsoup-1.12.1.txt deleted file mode 100644 index fa8a44e09e62..000000000000 --- a/apache-maven/src/main/appended-resources/licenses/MIT-jsoup-1.12.1.txt +++ /dev/null @@ -1,23 +0,0 @@ -https://raw.githubusercontent.com/jhy/jsoup/jsoup-1.12.1/LICENSE - -The MIT License - -Copyright (c) 2009-2019 Jonathan Hedley - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/apache-maven/src/main/appended-resources/licenses/MIT-slf4j-api-1.7.29.txt b/apache-maven/src/main/appended-resources/licenses/MIT-slf4j-api-1.7.29.txt deleted file mode 100644 index 5a09409f3638..000000000000 --- a/apache-maven/src/main/appended-resources/licenses/MIT-slf4j-api-1.7.29.txt +++ /dev/null @@ -1,23 +0,0 @@ -https://raw.githubusercontent.com/qos-ch/slf4j/v_1.7.29/LICENSE.txt - -Copyright (c) 2004-2017 QOS.ch -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/apache-maven/src/main/appended-resources/licenses/MIT.txt b/apache-maven/src/main/appended-resources/licenses/MIT.txt new file mode 100644 index 000000000000..d69be60416d5 --- /dev/null +++ b/apache-maven/src/main/appended-resources/licenses/MIT.txt @@ -0,0 +1,20 @@ + Copyright + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/apache-maven/src/main/appended-resources/licenses/Public-Domain.txt b/apache-maven/src/main/appended-resources/licenses/Public-Domain.txt new file mode 100644 index 000000000000..a7a158b93c32 --- /dev/null +++ b/apache-maven/src/main/appended-resources/licenses/Public-Domain.txt @@ -0,0 +1 @@ +Public Domain \ No newline at end of file diff --git a/apache-maven/src/site/apt/index.apt.vm b/apache-maven/src/site/apt/index.apt.vm new file mode 100644 index 000000000000..ae7ff8fd49ed --- /dev/null +++ b/apache-maven/src/site/apt/index.apt.vm @@ -0,0 +1,43 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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 +~~ +~~ http://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. + + ----- + ${project.name} + ----- + Hervé Boutemy + ----- + 2021-07-11 + ----- + +${project.name} + + ${project.description} + +* References + + * <<>> invocation can be customized through pre-invocation script calls, that can be disabled by setting <<>> environment variable: + +*----------+--------------------------------------------+-----------------------+ +|| || system level || user level +*----------+--------------------------------------------+-----------------------+ +|| POSIX | <<>>\ +|| | <<>> (since 3.8.2) | <<<$HOME/.mavenrc>>> +*----------+--------------------------------------------+-----------------------+ +|| Windows | <<<%PROGRAMDATA%\\mavenrc.cmd>>> (since 4) | <<<%USERPROFILE%\\mavenrc_pre.bat>>>\ +|| | | <<<%USERPROFILE%\\mavenrc_pre.cmd>>> (since 3.3.1)\ +|| | | <<<%USERPROFILE%\\mavenrc.cmd>>> (since 4) +*----------+--------------------------------------------+-----------------------+ diff --git a/apache-maven/src/site/site.xml b/apache-maven/src/site/site.xml index cd7807c147fd..c7d1796dcdec 100644 --- a/apache-maven/src/site/site.xml +++ b/apache-maven/src/site/site.xml @@ -19,13 +19,14 @@ specific language governing permissions and limitations under the License. --> - + ${project.scm.url} - - + + + - \ No newline at end of file + diff --git a/apache-maven/src/test/java/org/apache/maven/settings/GlobalSettingsTest.java b/apache-maven/src/test/java/org/apache/maven/settings/GlobalSettingsTest.java deleted file mode 100644 index 0d0543e2899d..000000000000 --- a/apache-maven/src/test/java/org/apache/maven/settings/GlobalSettingsTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.apache.maven.settings; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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. - */ - -import junit.framework.TestCase; -import org.apache.maven.settings.io.xpp3.SettingsXpp3Reader; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.io.Reader; - -/** - * Tests that the global settings.xml shipped with the distribution is in good state. - * - * @author Benjamin Bentmann - */ -public class GlobalSettingsTest - extends TestCase -{ - - public void testValidGlobalSettings() - throws Exception - { - String basedir = System.getProperty( "basedir", System.getProperty( "user.dir" ) ); - - File globalSettingsFile = new File( basedir, "src/conf/settings.xml" ); - assertTrue( globalSettingsFile.getAbsolutePath(), globalSettingsFile.isFile() ); - - try ( Reader reader = new InputStreamReader( new FileInputStream( globalSettingsFile ), "UTF-8" ) ) - { - new SettingsXpp3Reader().read( reader ); - } - } - -} diff --git a/api/maven-api-annotations/pom.xml b/api/maven-api-annotations/pom.xml new file mode 100644 index 000000000000..350873e35a30 --- /dev/null +++ b/api/maven-api-annotations/pom.xml @@ -0,0 +1,33 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-annotations + Maven 4 API :: Meta annotations + Maven 4 API - Java meta annotations. + + diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Config.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Config.java new file mode 100644 index 000000000000..6fe290478702 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Config.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation used to mark fields that represent configuration properties. + * This annotation provides metadata about how the configuration property + * should be handled, including its source, type, default value, and whether it's read-only. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.FIELD) +public @interface Config { + + /** + * Specifies the source of the configuration property, which determines when and where the property + * will be read from or set for consumption in the Maven build lifecycle. + * + * The source indicates whether the property is: + * - Set by Maven itself at startup (SYSTEM_PROPERTIES) + * - Configured by users through external means like CLI options (USER_PROPERTIES) + * - Defined in the project's POM file (MODEL) + * + * @return the source of the configuration property, defaults to USER_PROPERTIES + * @see Source for detailed information about each source type and when it's used + */ + Source source() default Source.USER_PROPERTIES; + + /** + * Specifies the type of the configuration property. + * + * @return the fully qualified class name of the property type, defaults to "java.lang.String" + */ + String type() default "java.lang.String"; + + /** + * Specifies the default value of the configuration property. + * + * @return the default value as a string, defaults to empty string + */ + String defaultValue() default ""; + + /** + * Specifies whether the configuration property is read-only. + * + * @return true if the property is read-only, false otherwise + */ + boolean readOnly() default false; + + /** + * Property source, which determines when and where the property will be read from or set for consumption. + * The source indicates the timing of property evaluation in the Maven build lifecycle and the location + * where the property value is defined. + */ + enum Source { + /** + * Maven system properties. These properties are evaluated very early during the boot process, + * typically set by Maven itself and flagged as readOnly=true or by users via maven-system.properties files. + * System properties are initialized before the build starts and are available throughout the entire Maven + * execution. They are used for core Maven functionality that needs to be established at startup. + */ + SYSTEM_PROPERTIES, + /** + * Maven user properties. These are properties that users configure through various means such as + * maven-user.properties files, maven.config files, command line parameters (-D flags), settings.xml, + * or environment variables. They are evaluated during the build process and represent the primary + * way for users to customize Maven's behavior at runtime. + */ + USER_PROPERTIES, + /** + * Project model properties. These properties are defined in the project's POM file (pom.xml) and + * are read from the project model during the build. They represent build-time configuration that + * is specific to the project and is stored with the project definition itself rather than in + * external configuration. + */ + MODEL + } +} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Consumer.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Consumer.java new file mode 100644 index 000000000000..63e720c62e1f --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Consumer.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A type implemented by, or extended by maven plugins or extensions. + * Maven plugins or extensions may provide implementations of those types which will be used by maven. + *

+ * A type can be marked {@link Consumer} or {@link Provider} but not both. A type is assumed to be + * {@link Consumer} if it is not marked either {@link Consumer} or {@link Provider}. + *

+ * A package can be marked {@link Consumer}. In this case, all types in the package are considered + * to be a provider type regardless of whether they are marked {@link Consumer} or {@link Provider}. + * + * @see Provider + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.PACKAGE}) +public @interface Consumer {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Experimental.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Experimental.java new file mode 100644 index 000000000000..6554cea29421 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Experimental.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This annotation tags types that are part of an experimental API. + * Classes or methods annotated with this annotation may be changed / removed without notice. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface Experimental {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Generated.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Generated.java new file mode 100644 index 000000000000..d41ba1cbc00b --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Generated.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation indicates that a type is automatically generated. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface Generated {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java new file mode 100644 index 000000000000..e86d17fbedc3 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@code Immutable} annotation indicates that the object is immutable, i.e. + * none of its field can be changed. This also ensures that the type is + * {@link ThreadSafe}. + * + * @see ThreadSafe + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@ThreadSafe +@Target(ElementType.TYPE) +public @interface Immutable {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Nonnull.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Nonnull.java new file mode 100644 index 000000000000..ada5c0dceff4 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Nonnull.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The annotated element must not be null. + *

+ * Annotated fields must not be null after construction has completed. + *

+ * When this annotation is applied to a method it applies to the method return value. + * + * @see Nullable + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) +public @interface Nonnull {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/NotThreadSafe.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/NotThreadSafe.java new file mode 100644 index 000000000000..348ca77a35b8 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/NotThreadSafe.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation indicates that the annotated type is not threadsafe + * and should only be used by a single thread. + * + * @see ThreadSafe + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface NotThreadSafe {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Nullable.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Nullable.java new file mode 100644 index 000000000000..df2517cad9aa --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Nullable.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The annotated element can be {@code null}. + * + * @see Nonnull + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface Nullable {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Provider.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Provider.java new file mode 100644 index 000000000000..69d73db7c8f2 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Provider.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A type implemented by, or extended by maven itself. + * Maven provides implementations of those types and may inject them in plugins. + *

+ * A type can be marked {@link Consumer} or {@link Provider} but not both. A type is assumed to be + * {@link Consumer} if it is not marked either {@link Consumer} or {@link Provider}. + *

+ * A package can be marked {@link Provider}. In this case, all types in the package are considered + * to be a provider type regardless of whether they are marked {@link Consumer} or {@link Provider}. + * + * @see Consumer + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.PACKAGE}) +public @interface Provider {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/ThreadSafe.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/ThreadSafe.java new file mode 100644 index 000000000000..9b500c2f4340 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/ThreadSafe.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@code ThreadSafe} annotation can be used to indicate a given type + * is thread safe. {@link Immutable} objects are automatically thread safe. + * + * @see Immutable + * @see NotThreadSafe + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface ThreadSafe {} diff --git a/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/package-info.java b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/package-info.java new file mode 100644 index 000000000000..220a5c381453 --- /dev/null +++ b/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/package-info.java @@ -0,0 +1,10 @@ +// CHECKSTYLE_OFF: RegexpHeader +/** + * This package contains non-functional annotations which are + * used to tag various elements and help users understanding + * how those types should be used. + * + * @since 4.0.0 + */ +@Experimental +package org.apache.maven.api.annotations; diff --git a/api/maven-api-annotations/src/site/site.xml b/api/maven-api-annotations/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-annotations/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + +

+ + + + + + + + + + diff --git a/api/maven-api-cli/pom.xml b/api/maven-api-cli/pom.xml new file mode 100644 index 000000000000..6ed5c71eff71 --- /dev/null +++ b/api/maven-api-cli/pom.xml @@ -0,0 +1,83 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-cli + Maven 4 API :: CLI + Maven 4 API - CLI. + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-core + + + org.apache.maven + maven-api-xml + + + + + + + org.codehaus.modello + modello-maven-plugin + + 1.2.0 + + src/main/mdo/core-extensions.mdo + + + + + + locationTracking=true + generateLocationClasses=true + packageModelV4=org.apache.maven.api.cli.extensions + packageToolV4=org.apache.maven.cli.internal.extension.io + + ${project.basedir}/../../src/mdo + + + + modello + + velocity + xdoc + xsd + + generate-sources + + + + + + diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/CoreExtensions.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/CoreExtensions.java new file mode 100644 index 000000000000..8206b26b0a95 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/CoreExtensions.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import java.nio.file.Path; +import java.util.List; + +import org.apache.maven.api.Constants; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.cli.extensions.CoreExtension; + +import static java.util.Objects.requireNonNull; + +/** + * Represents the list of core extensions configured at one source. The list is validated (are GA unique), but no + * other logic than that is applied. + * + * @since 4.0.0 + * @param source The source file of core extensions, is never {@code null}. + * @param coreExtensions The configured core extensions, is never {@code null}. Contents of list is guaranteed to be unique by GA. + * + * @see Constants#MAVEN_PROJECT_EXTENSIONS + * @see Constants#MAVEN_USER_EXTENSIONS + * @see Constants#MAVEN_INSTALLATION_EXTENSIONS + */ +@Experimental +public record CoreExtensions(Path source, List coreExtensions) { + public CoreExtensions(Path source, List coreExtensions) { + this.source = requireNonNull(source, "source"); + this.coreExtensions = requireNonNull(coreExtensions, "coreExtensions"); + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java new file mode 100644 index 000000000000..3c4cb05ecaab --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Defines the contract for a component responsible for invoking a Maven application + * using the information provided in an {@link InvokerRequest}. This interface is central + * to the construction and invocation of Maven commands and builds, and it fully parses arguments. + * + *

The Invoker is designed to be flexible, allowing for different implementations + * that can handle various types of {@link InvokerRequest InvokerRequests}. It also implements + * {@link AutoCloseable} to ensure proper resource management.

+ * + * @since 4.0.0 + */ +@Experimental +public interface Invoker extends AutoCloseable { + /** + * Invokes the Maven application using the provided {@link InvokerRequest}. + * This method is responsible for executing the Maven command or build + * process based on the information contained in the request. + * + * @param invokerRequest the request containing all necessary information for the invocation + * @return an integer representing the exit code of the invocation (0 typically indicates success) + * @throws InvokerException if an error occurs during the invocation process. + */ + int invoke(@Nonnull InvokerRequest invokerRequest) throws InvokerException; + + /** + * Closes and disposes of this {@link Invoker} instance, releasing any resources it may hold. + * This method is called automatically when using try-with-resources statements. + * + *

The default implementation does nothing. Subclasses should override this method + * if they need to perform cleanup operations.

+ * + * @throws InvokerException if an error occurs while closing the {@link Invoker} + */ + @Override + default void close() throws InvokerException {} +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java new file mode 100644 index 000000000000..d1a479b71127 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.services.MavenException; + +/** + * Represents an exception that occurs during the invocation of a Maven build or command. + * This exception is typically thrown when there are errors during the execution of a Maven + * process, such as build failures, plugin errors, or other runtime issues. + * + * @since 4.0.0 + */ +@Experimental +public class InvokerException extends MavenException { + /** + * Constructs a new {@code InvokerException} with the specified detail message. + * + * @param message the detail message explaining the cause of the exception + */ + public InvokerException(@Nullable String message) { + super(message); + } + + /** + * Constructs a new {@code InvokerException} with the specified detail message and cause. + * + * @param message the detail message explaining the cause of the exception + * @param cause the underlying cause of the exception + */ + public InvokerException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } + + /** + * Exception for intentional exit: No message or anything will be displayed, just the + * carried exit code will be returned from invoker {@link Invoker#invoke(InvokerRequest)} method. + */ + public static final class ExitException extends InvokerException { + private final int exitCode; + + public ExitException(int exitCode) { + super("EXIT"); + this.exitCode = exitCode; + } + + public int getExitCode() { + return exitCode; + } + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java new file mode 100644 index 000000000000..e63a515bb9ff --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.cli.cisupport.CIInfo; +import org.apache.maven.api.services.Lookup; +import org.apache.maven.api.services.MessageBuilderFactory; + +/** + * Represents a Maven invocation request, encapsulating all necessary information + * for invoking a Maven build or command. Arguments are parsed and exposed via methods. + * + * @since 4.0.0 + */ +@Immutable +@Experimental +public interface InvokerRequest { + /** + * The parser request this instance was created from. + */ + @Nonnull + ParserRequest parserRequest(); + + /** + * Flag representing parser processing result: if there were some fatal errors during + * {@link Parser#parseInvocation(ParserRequest)} this method will return {@code true} and invoker should + * handle this request as "early failure". In these cases, {@link #options()} usually is absent. + */ + boolean parsingFailed(); + + /** + * Returns {@code true} if this call happens in "embedded" mode. + * + * @see ParserRequest#embedded() + */ + default boolean embedded() { + return parserRequest().embedded(); + } + + /** + * Returns the current working directory for the Maven execution. + * This is typically the directory from which Maven was invoked. + * + * @return the current working directory path + */ + @Nonnull + Path cwd(); + + /** + * Returns the Maven installation directory. + * This is usually set by the Maven launcher script using the "maven.home" system property. + * + * @return the Maven installation directory path + */ + @Nonnull + Path installationDirectory(); + + /** + * Returns the user's home directory. + * This is typically obtained from the "user.home" system property. + * + * @return the user's home directory path + */ + @Nonnull + Path userHomeDirectory(); + + /** + * Shorthand for {@link MessageBuilderFactory}. + */ + default MessageBuilderFactory messageBuilderFactory() { + return parserRequest().messageBuilderFactory(); + } + + /** + * Shorthand for {@link Lookup}. + */ + default Lookup lookup() { + return parserRequest().lookup(); + } + + /** + * Returns a map of user-defined properties for the Maven execution. + * These properties can be set using the -D command-line option. + * + * @return an unmodifiable map of user properties + */ + @Nonnull + Map userProperties(); + + /** + * Returns a map of system properties for the Maven execution. + * These include both Java system properties and Maven-specific system properties. + * + * @return an unmodifiable map of system properties + */ + @Nonnull + Map systemProperties(); + + /** + * Returns the top-level directory of the Maven invocation. + * This is typically the directory containing the POM file being executed. + * + * @return the top-level directory path + */ + @Nonnull + Path topDirectory(); + + /** + * Returns the root directory of the Maven invocation, if found. This is determined by the presence of a + * {@code .mvn} directory or a POM with the root="true" property but is not always applicable (ie invocation + * from outside a checkout). + * + * @return the root directory path, if present + */ + @Nonnull + Optional rootDirectory(); + + /** + * Returns the input stream for the Maven execution, if running in embedded mode. + * + * @return an {@link Optional} containing the input stream, or empty if not applicable + */ + @Nonnull + default Optional stdIn() { + return Optional.ofNullable(parserRequest().stdIn()); + } + + /** + * Returns the output stream for the Maven execution, if running in embedded mode. + * + * @return an {@link Optional} containing the output stream, or empty if not applicable + */ + @Nonnull + default Optional stdOut() { + return Optional.ofNullable(parserRequest().stdOut()); + } + + /** + * Returns the error stream for the Maven execution, if running in embedded mode. + * + * @return an {@link Optional} containing the error stream, or empty if not applicable + */ + @Nonnull + default Optional stdErr() { + return Optional.ofNullable(parserRequest().stdErr()); + } + + /** + * Returns a list of core extensions from all sources, that were discovered and loaded. Each instance of + * {@link CoreExtensions} is validated, but the list elements may have overlapping elements, that requires + * some logic to sort out (like precedence). + *

+ * The list of {@link CoreExtensions} if present, is in precedence order. + * + * @return an {@link Optional} containing the {@link CoreExtensions}, or empty if not configured + */ + @Nonnull + Optional> coreExtensions(); + + /** + * Returns detected CI system, if any. + * + * @return an {@link Optional} containing the {@link CIInfo} collected from CI system. or empty if CI not + * detected. + */ + @Nonnull + Optional ciInfo(); + + /** + * Returns the options associated with this invocation request. + * + * @return the options optional. It will be absent if {@link #parsingFailed()} return {@code true}. + */ + @Nonnull + Optional options(); + + /** + * This method returns "verbose" option value derived from multiple places: CLI options, but also CI detection, + * if applicable. + */ + default boolean effectiveVerbose() { + return options().isPresent() && options().orElseThrow().verbose().orElse(false) + || ciInfo().isPresent() && ciInfo().orElseThrow().isVerbose(); + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Logger.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Logger.java new file mode 100644 index 000000000000..cd9aaff994e0 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Logger.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import java.util.List; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Defines a simple logging interface for Maven CLI operations. These operations happen "early", when there may + * be no logging set up even. Implementations may be "accumulating", in which case {@link #drain()} method should + * be used. + *

+ * This interface provides methods for logging messages at different severity levels + * and supports logging with or without associated exceptions. + * + * @since 4.0.0 + */ +@Experimental +public interface Logger { + + /** + * Represents the severity levels for log messages. + */ + enum Level { + DEBUG, + INFO, + WARN, + ERROR + } + + /** + * Logs a message at the specified level without an associated exception. + * + * @param level the severity level of the message + * @param message the message to be logged + */ + default void log(@Nonnull Level level, @Nonnull String message) { + log(level, message, null); + } + + /** + * Logs a message at the specified level with an associated exception. + * + * @param level the severity level of the message + * @param message the message to be logged + * @param error the associated exception, or null if not applicable + */ + void log(@Nonnull Level level, @Nonnull String message, @Nullable Throwable error); + + /** + * Logs a debug message without an associated exception. + * + * @param message the debug message to be logged + */ + default void debug(String message) { + log(Level.DEBUG, message); + } + + /** + * Logs a debug message with an associated exception. + * + * @param message the debug message to be logged + * @param error the associated exception + */ + default void debug(@Nonnull String message, @Nullable Throwable error) { + log(Level.DEBUG, message, error); + } + + /** + * Logs an info message without an associated exception. + * + * @param message the info message to be logged + */ + default void info(@Nonnull String message) { + log(Level.INFO, message); + } + + /** + * Logs an info message with an associated exception. + * + * @param message the info message to be logged + * @param error the associated exception + */ + default void info(@Nonnull String message, @Nullable Throwable error) { + log(Level.INFO, message, error); + } + + /** + * Logs a warning message without an associated exception. + * + * @param message the warning message to be logged + */ + default void warn(@Nonnull String message) { + log(Level.WARN, message); + } + + /** + * Logs a warning message with an associated exception. + * + * @param message the warning message to be logged + * @param error the associated exception + */ + default void warn(@Nonnull String message, @Nullable Throwable error) { + log(Level.WARN, message, error); + } + + /** + * Logs an error message without an associated exception. + * + * @param message the error message to be logged + */ + default void error(@Nonnull String message) { + log(Level.ERROR, message); + } + + /** + * Logs an error message with an associated exception. + * + * @param message the error message to be logged + * @param error the associated exception + */ + default void error(@Nonnull String message, @Nullable Throwable error) { + log(Level.ERROR, message, error); + } + + /** + * Logger entries returned by {@link #drain()} method. + * @param level The logging level, never {@code null}. + * @param message The logging message, never {@code null}. + * @param error The error, if applicable. + */ + record Entry(@Nonnull Level level, @Nonnull String message, @Nullable Throwable error) {} + + /** + * If this is an accumulating log, it will "drain" this instance. It returns the accumulated log entries, and + * also "resets" this instance to empty (initial) state. + */ + @Nonnull + default List drain() { + return List.of(); + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java new file mode 100644 index 000000000000..d2bf596cd916 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Represents the base options supported by Maven tools. + * This interface defines methods to access various configuration options + * that can be set through command-line arguments or configuration files. + * + * @since 4.0.0 + */ +@Experimental +public interface Options { + /** Constant indicating that the options source is the command-line interface. */ + String SOURCE_CLI = "CLI"; + + /** + * Returns a simple designator of the options source, such as "cli", "maven.conf", etc. + * + * @return a string representing the source of the options + */ + @Nonnull + String source(); + + /** + * Returns the user-defined properties for the Maven execution. + * + * @return an {@link Optional} containing the map of user properties, or empty if not set + */ + @Nonnull + Optional> userProperties(); + + /** + * Indicates whether to show the version information and exit. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional showVersionAndExit(); + + /** + * Indicates whether to show the version information. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional showVersion(); + + /** + * Indicates whether to run in quiet mode. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional quiet(); + + /** + * Indicates whether to run in verbose mode. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional verbose(); + + /** + * Indicates whether to show error stack traces. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional showErrors(); + + /** + * Returns the severity level at which the build should fail. + * + * @return an {@link Optional} containing the fail-on-severity string, or empty if not set + */ + @Nonnull + Optional failOnSeverity(); + + /** + * Indicates whether to run in non-interactive mode. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional nonInteractive(); + + /** + * Indicates whether to force interactive mode. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional forceInteractive(); + + /** + * Returns the path to an alternate user settings file. + * + * @return an {@link Optional} containing the file path, or empty if not set + */ + @Nonnull + Optional altUserSettings(); + + /** + * Returns the path to an alternate project settings file. + * + * @return an {@link Optional} containing the file path, or empty if not set + */ + @Nonnull + Optional altProjectSettings(); + + /** + * Returns the path to an alternate installation settings file. + * + * @return an {@link Optional} containing the file path, or empty if not set + */ + @Nonnull + Optional altInstallationSettings(); + + /** + * Returns the path to an alternate user toolchains file. + * + * @return an {@link Optional} containing the file path, or empty if not set + */ + @Nonnull + Optional altUserToolchains(); + + /** + * Returns the path to an alternate installation toolchains file. + * + * @return an {@link Optional} containing the file path, or empty if not set + */ + @Nonnull + Optional altInstallationToolchains(); + + /** + * Returns the path to the log file. + * + * @return an {@link Optional} containing the file path, or empty if not set + */ + @Nonnull + Optional logFile(); + + /** + * Returns whether raw streams should be logged. + * + * @return a boolean indicating whether raw streams should be logged + */ + @Nonnull + Optional rawStreams(); + + /** + * Returns the color setting for console output. + * + * @return an {@link Optional} containing the color setting, or empty if not set + */ + @Nonnull + Optional color(); + + /** + * Indicates whether Maven should operate in offline mode. + * + * @return an {@link Optional} containing true if offline mode is enabled, false if disabled, or empty if not specified + */ + @Nonnull + Optional offline(); + + /** + * Indicates whether to show help information. + * + * @return an {@link Optional} containing the boolean flag, or empty if not set + */ + @Nonnull + Optional help(); + + /** + * Returns a new instance of {@link Options} with values interpolated using the given properties. + * + * @param callback the callback to use for interpolation + * @return a new {@link Options} instance with interpolated values + */ + @Nonnull + Options interpolate(@Nonnull UnaryOperator callback); + + /** + * Emits warning messages if deprecated options are used. + * + * @param printWriter the string consumer to use for output + */ + default void warnAboutDeprecatedOptions(@Nonnull ParserRequest request, @Nonnull Consumer printWriter) {} + + /** + * Displays help information for these options. + * + * @param printWriter the string consumer to use for output + */ + void displayHelp(@Nonnull ParserRequest request, @Nonnull Consumer printWriter); +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java new file mode 100644 index 000000000000..dc190800cdd5 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Defines the contract for parsing Maven command-line arguments and creating an execution or invoker requests. + * + * @since 4.0.0 + */ +@Experimental +public interface Parser { + /** + * Parses the given ParserRequest to create an {@link InvokerRequest}. + * This method does interpret tool arguments. + * + * @param parserRequest the request containing all necessary information for parsing + * @return the parsed invoker request. Caller must start by checking {@link InvokerRequest#parsingFailed()} as + * if there are parser errors, this request may not be fully processed and should immediately be failed. + */ + @Nonnull + InvokerRequest parseInvocation(@Nonnull ParserRequest parserRequest); +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java new file mode 100644 index 000000000000..ee25ec63dab3 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java @@ -0,0 +1,499 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.cli.logging.AccumulatingLogger; +import org.apache.maven.api.services.Lookup; +import org.apache.maven.api.services.LookupException; +import org.apache.maven.api.services.MessageBuilderFactory; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a request to parse Maven command-line arguments. + * This interface encapsulates all the necessary information needed to parse + * Maven commands and arguments into an {@link InvokerRequest}. + * + * @since 4.0.0 + */ +@Immutable +@Experimental +public interface ParserRequest { + /** + * Returns the Maven command to be executed. This command is used in some invokers (ie forked) but also to + * present help to user. + * + * @return the command string + */ + @Nonnull + String command(); + + /** + * Returns the Maven command name (ie "Maven"). This string is used in some invokers to complete error messages. + * + * @return the command (human) name + */ + @Nonnull + String commandName(); + + /** + * Returns the logger to be used during the parsing process. + * + * @return the logger instance + */ + @Nonnull + Logger logger(); + + /** + * Returns the factory for creating message builders. + * + * @return the message builder factory + */ + @Nonnull + MessageBuilderFactory messageBuilderFactory(); + + /** + * Returns the command-line arguments to be parsed. + * + * @return a list of argument strings + */ + @Nonnull + List args(); + + /** + * Per-request {@link Lookup} for customization. + * + * @return a lookup possibly with custom components + */ + @Nonnull + Lookup lookup(); + + /** + * Returns the current working directory for the Maven execution. + * If not explicitly set, this value will be detected during parsing. + * + * @return the current working directory path, or null if not set + */ + @Nullable + Path cwd(); + + /** + * Returns the Maven home directory. + * If not explicitly set, this value will be detected during parsing. + * + * @return the Maven home directory path, or null if not set + */ + @Nullable + Path mavenHome(); + + /** + * Returns the user's home directory. + * If not explicitly set, this value will be detected during parsing. + * + * @return the user's home directory path, or null if not set + */ + @Nullable + Path userHome(); + + /** + * Returns the input stream to be used for the Maven execution. + * If not set, {@link System#in} will be used by default. + * + * @return the input stream, or null if not set + */ + @Nullable + InputStream stdIn(); + + /** + * Returns the output stream to be used for the Maven execution. + * If not set, {@link System#out} will be used by default. + * + * @return the output stream, or null if not set + */ + @Nullable + OutputStream stdOut(); + + /** + * Returns the error stream to be used for the Maven execution. + * If not set, {@link System#err} will be used by default. + * + * @return the error stream, or null if not set + */ + @Nullable + OutputStream stdErr(); + + /** + * Returns {@code true} if this call happens in "embedded" mode, for example by another application that + * embeds Maven. When running in "embedded" mode, Maven will not try to grab system terminal and will use + * provided {@link #stdIn()} or {@link InputStream#nullInputStream()} as standard in stream. + */ + boolean embedded(); + + /** + * Creates a new Builder instance for constructing a Maven ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvn(@Nonnull String[] args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return mvn(Arrays.asList(args), messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvn(@Nonnull List args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return builder(Tools.MVN_CMD, Tools.MVN_NAME, args, messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven Encrypting Tool ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvnenc(@Nonnull String[] args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return mvnenc(Arrays.asList(args), messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven Encrypting Tool ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvnenc(@Nonnull List args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return builder(Tools.MVNENC_CMD, Tools.MVNENC_NAME, args, messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven Shell Tool ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvnsh(@Nonnull String[] args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return mvnsh(Arrays.asList(args), messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven Shell Tool ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvnsh(@Nonnull List args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return builder(Tools.MVNSHELL_CMD, Tools.MVNSHELL_NAME, args, messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven Upgrade Tool ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvnup(@Nonnull String[] args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return mvnup(Arrays.asList(args), messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a Maven Upgrade Tool ParserRequest. + * + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder mvnup(@Nonnull List args, @Nonnull MessageBuilderFactory messageBuilderFactory) { + return builder(Tools.MVNUP_CMD, Tools.MVNUP_NAME, args, messageBuilderFactory); + } + + /** + * Creates a new Builder instance for constructing a ParserRequest. + * + * @param command the Maven command to be executed + * @param commandName the Maven command Name to be executed + * @param args the command-line arguments + * @param messageBuilderFactory the factory for creating message builders + * @return a new Builder instance + */ + @Nonnull + static Builder builder( + @Nonnull String command, + @Nonnull String commandName, + @Nonnull List args, + @Nonnull MessageBuilderFactory messageBuilderFactory) { + return new Builder(command, commandName, args, messageBuilderFactory); + } + + class Builder { + private final String command; + private final String commandName; + private final List args; + private final MessageBuilderFactory messageBuilderFactory; + + private final Logger logger; + private Lookup lookup = EMPTY_LOOKUP; + private Path cwd; + private Path mavenHome; + private Path userHome; + private InputStream stdIn; + private OutputStream stdOut; + private OutputStream stdErr; + private boolean embedded = false; + + private Builder( + String command, String commandName, List args, MessageBuilderFactory messageBuilderFactory) { + this.command = requireNonNull(command, "command"); + this.commandName = requireNonNull(commandName, "commandName"); + this.args = requireNonNull(args, "args"); + this.messageBuilderFactory = requireNonNull(messageBuilderFactory, "messageBuilderFactory"); + this.logger = new AccumulatingLogger(); + } + + public Builder lookup(@Nonnull Lookup lookup) { + this.lookup = requireNonNull(lookup); + return this; + } + + public Builder cwd(Path cwd) { + this.cwd = cwd; + return this; + } + + public Builder mavenHome(Path mavenHome) { + this.mavenHome = mavenHome; + return this; + } + + public Builder userHome(Path userHome) { + this.userHome = userHome; + return this; + } + + public Builder stdIn(InputStream stdIn) { + this.stdIn = stdIn; + return this; + } + + public Builder stdOut(OutputStream stdOut) { + this.stdOut = stdOut; + return this; + } + + public Builder stdErr(OutputStream stdErr) { + this.stdErr = stdErr; + return this; + } + + public Builder embedded(boolean embedded) { + this.embedded = embedded; + return this; + } + + public ParserRequest build() { + return new ParserRequestImpl( + command, + commandName, + List.copyOf(args), + lookup.lookupOptional(Logger.class).orElse(logger), + messageBuilderFactory, + lookup, + cwd, + mavenHome, + userHome, + stdIn, + stdOut, + stdErr, + embedded); + } + + @SuppressWarnings("ParameterNumber") + private static class ParserRequestImpl implements ParserRequest { + private final String command; + private final String commandName; + private final Logger logger; + private final MessageBuilderFactory messageBuilderFactory; + private final List args; + private final Lookup lookup; + private final Path cwd; + private final Path mavenHome; + private final Path userHome; + private final InputStream stdIn; + private final OutputStream stdOut; + private final OutputStream stdErr; + private final boolean embedded; + + private ParserRequestImpl( + String command, + String commandName, + List args, + Logger logger, + MessageBuilderFactory messageBuilderFactory, + Lookup lookup, + Path cwd, + Path mavenHome, + Path userHome, + InputStream stdIn, + OutputStream stdOut, + OutputStream stdErr, + boolean embedded) { + this.command = requireNonNull(command, "command"); + this.commandName = requireNonNull(commandName, "commandName"); + this.args = List.copyOf(requireNonNull(args, "args")); + this.logger = requireNonNull(logger, "logger"); + this.messageBuilderFactory = requireNonNull(messageBuilderFactory, "messageBuilderFactory"); + this.lookup = requireNonNull(lookup, "lookup"); + this.cwd = cwd; + this.mavenHome = mavenHome; + this.userHome = userHome; + this.stdIn = stdIn; + this.stdOut = stdOut; + this.stdErr = stdErr; + this.embedded = embedded; + } + + @Override + public String command() { + return command; + } + + @Override + public String commandName() { + return commandName; + } + + @Override + public Logger logger() { + return logger; + } + + @Override + public MessageBuilderFactory messageBuilderFactory() { + return messageBuilderFactory; + } + + @Override + public List args() { + return args; + } + + @Override + public Lookup lookup() { + return lookup; + } + + @Override + public Path cwd() { + return cwd; + } + + @Override + public Path mavenHome() { + return mavenHome; + } + + @Override + public Path userHome() { + return userHome; + } + + @Override + public InputStream stdIn() { + return stdIn; + } + + @Override + public OutputStream stdOut() { + return stdOut; + } + + @Override + public OutputStream stdErr() { + return stdErr; + } + + @Override + public boolean embedded() { + return embedded; + } + } + + private static final Lookup EMPTY_LOOKUP = new Lookup() { + @Override + public T lookup(Class type) { + throw new LookupException("empty lookup"); + } + + @Override + public T lookup(Class type, String name) { + throw new LookupException("empty lookup"); + } + + @Override + public Optional lookupOptional(Class type) { + return Optional.empty(); + } + + @Override + public Optional lookupOptional(Class type, String name) { + return Optional.empty(); + } + + @Override + public List lookupList(Class type) { + return List.of(); + } + + @Override + public Map lookupMap(Class type) { + return Map.of(); + } + }; + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Tools.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Tools.java new file mode 100644 index 000000000000..7559d7ffee06 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Tools.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; + +/** + * Represents most common tools supported by CLIng. + * + * @since 4.0.0 + */ +@Immutable +@Experimental +public final class Tools { + private Tools() {} + + public static final String MVN_CMD = "mvn"; + public static final String MVN_NAME = "Maven"; + + public static final String MVNENC_CMD = "mvnenc"; + public static final String MVNENC_NAME = "Maven Password Encrypting Tool"; + + public static final String MVNSHELL_CMD = "mvnsh"; + public static final String MVNSHELL_NAME = "Maven Shell Tool"; + + public static final String MVNUP_CMD = "mvnup"; + public static final String MVNUP_NAME = "Maven Upgrade Tool"; +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/cisupport/CIInfo.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/cisupport/CIInfo.java new file mode 100644 index 000000000000..ced54f837385 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/cisupport/CIInfo.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli.cisupport; + +import org.apache.maven.api.annotations.Nonnull; + +/** + * CI support: this class contains gathered information and more from CI that Maven process runs on. + * + * @since 4.0.0 + */ +public interface CIInfo { + /** + * Short distinct name of CI system: "GH", "Jenkins", etc. + */ + @Nonnull + String name(); + + /** + * May return a message that will be logged by Maven explaining why it was detected (and possibly more). + */ + @Nonnull + default String message() { + return ""; + } + + /** + * Some CI systems may allow running jobs in "debug" (or some equivalent) mode. + */ + default boolean isVerbose() { + return false; + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/extensions/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/extensions/package-info.java new file mode 100644 index 000000000000..65b9a662b5d7 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/extensions/package-info.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides support for Maven core extensions configuration and management. + * + *

This package contains classes for:

+ *
    + *
  • Extension configuration model
  • + *
  • Extension loading and validation
  • + *
  • Extension lifecycle management
  • + *
+ * + * @since 4.0.0 + */ +package org.apache.maven.api.cli.extensions; diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/logging/AccumulatingLogger.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/logging/AccumulatingLogger.java new file mode 100644 index 000000000000..013e73a81ae9 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/logging/AccumulatingLogger.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli.logging; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.maven.api.cli.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * Early CLI {@link Logger} that simply accumulates log entries until some point a real logger can emit them. This + * logger is created at start, and it exists while no logging is available yet. + */ +public class AccumulatingLogger implements Logger { + private final AtomicReference> entries = new AtomicReference<>(new CopyOnWriteArrayList<>()); + + @Override + public void log(Level level, String message, Throwable error) { + requireNonNull(level, "level"); + requireNonNull(message, "message"); + entries.get().add(new Entry(level, message, error)); + } + + @Override + public List drain() { + return entries.getAndSet(new CopyOnWriteArrayList<>()); + } +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/logging/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/logging/package-info.java new file mode 100644 index 000000000000..e2db461d8836 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/logging/package-info.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides early-stage logging capabilities for Maven CLI operations. + * These logging facilities are used during Maven startup before the full logging + * system is initialized. + * + *

This package includes:

+ *
    + *
  • Basic logging interfaces and implementations
  • + *
  • Support for different logging levels (DEBUG, INFO, WARN, ERROR)
  • + *
  • Accumulating loggers for deferred output
  • + *
+ * + * @since 4.0.0 + */ +package org.apache.maven.api.cli.logging; diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenOptions.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenOptions.java new file mode 100644 index 000000000000..a77ea4ebb7e0 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenOptions.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli.mvn; + +import java.util.List; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.cli.Options; + +/** + * Defines the options specific to Maven operations. + * This interface extends the general {@link Options} interface, adding Maven-specific configuration options. + * + *

These options represent the various flags and settings available through the Maven CLI, + * as well as those that can be specified in the {@code maven.config} file. They provide fine-grained + * control over Maven's behavior during the build process.

+ * + * @since 4.0.0 + */ +@Experimental +public interface MavenOptions extends Options { + + /** + * Returns the path to an alternate POM file. + * + * @return an {@link Optional} containing the path to the alternate POM file, or empty if not specified + */ + @Nonnull + Optional alternatePomFile(); + + /** + * Indicates whether Maven should operate in non-recursive mode (i.e., not build child modules). + * + * @return an {@link Optional} containing true if non-recursive mode is enabled, false if disabled, or empty if not specified + */ + @Nonnull + Optional nonRecursive(); + + /** + * Indicates whether Maven should force a check for updated snapshots on remote repositories. + * + * @return an {@link Optional} containing true if snapshot updates should be forced, false if not, or empty if not specified + */ + @Nonnull + Optional updateSnapshots(); + + /** + * Returns the list of profiles to activate. + * + * @return an {@link Optional} containing the list of profile names to activate, or empty if not specified + */ + @Nonnull + Optional> activatedProfiles(); + + /** + * Indicates whether Maven should suppress SNAPSHOT updates. + * + * @return an {@link Optional} containing true if SNAPSHOT updates should be suppressed, false if not, or empty if not specified + */ + @Nonnull + Optional suppressSnapshotUpdates(); + + /** + * Indicates whether Maven should use strict checksum verification. + * + * @return an {@link Optional} containing true if strict checksum verification is enabled, false if not, or empty if not specified + */ + @Nonnull + Optional strictChecksums(); + + /** + * Indicates whether Maven should use relaxed checksum verification. + * + * @return an {@link Optional} containing true if relaxed checksum verification is enabled, false if not, or empty if not specified + */ + @Nonnull + Optional relaxedChecksums(); + + /** + * Indicates whether Maven should stop at the first failure in a multi-module build. + * + * @return an {@link Optional} containing true if Maven should stop at the first failure, false if not, or empty if not specified + */ + @Nonnull + Optional failFast(); + + /** + * Indicates whether Maven should run all builds but defer error reporting to the end. + * + * @return an {@link Optional} containing true if error reporting should be deferred to the end, false if not, or empty if not specified + */ + @Nonnull + Optional failAtEnd(); + + /** + * Indicates whether Maven should never fail the build, regardless of project result. + * + * @return an {@link Optional} containing true if the build should never fail, false if it should fail normally, or empty if not specified + */ + @Nonnull + Optional failNever(); + + /** + * Indicates whether Maven should resume from the last failed project in a previous build. + * + * @return an {@link Optional} containing true if Maven should resume from the last failure, false if not, or empty if not specified + */ + @Nonnull + Optional resume(); + + /** + * Returns the project to resume the build from. + * + * @return an {@link Optional} containing the project name to resume from, or empty if not specified + */ + @Nonnull + Optional resumeFrom(); + + /** + * Returns the list of specified reactor projects to build instead of all projects. + * + * @return an {@link Optional} containing the list of project names to build, or empty if not specified + */ + @Nonnull + Optional> projects(); + + /** + * Indicates whether Maven should also build the specified projects' dependencies. + * + * @return an {@link Optional} containing true if dependencies should also be built, false if not, or empty if not specified + */ + @Nonnull + Optional alsoMake(); + + /** + * Indicates whether Maven should also build the specified projects' dependents. + * + * @return an {@link Optional} containing true if dependents should also be built, false if not, or empty if not specified + */ + @Nonnull + Optional alsoMakeDependents(); + + /** + * Returns the number of threads used for parallel builds. + * + * @return an {@link Optional} containing the number of threads (or "1C" for one thread per CPU core), or empty if not specified + */ + @Nonnull + Optional threads(); + + /** + * Returns the id of the build strategy to use. + * + * @return an {@link Optional} containing the id of the build strategy, or empty if not specified + */ + @Nonnull + Optional builder(); + + /** + * Indicates whether Maven should not display transfer progress when downloading or uploading. + * + * @return an {@link Optional} containing true if transfer progress should not be displayed, false if it should, or empty if not specified + */ + @Nonnull + Optional noTransferProgress(); + + /** + * Indicates whether Maven should cache the "not found" status of artifacts that were not found in remote repositories. + * + * @return an {@link Optional} containing true if "not found" status should be cached, false if not, or empty if not specified + */ + @Nonnull + Optional cacheArtifactNotFound(); + + /** + * Indicates whether Maven should use strict artifact descriptor policy. + * + * @return an {@link Optional} containing true if strict artifact descriptor policy should be used, false if not, or empty if not specified + */ + @Nonnull + Optional strictArtifactDescriptorPolicy(); + + /** + * Indicates whether Maven should ignore transitive repositories. + * + * @return an {@link Optional} containing true if transitive repositories should be ignored, false if not, or empty if not specified + */ + @Nonnull + Optional ignoreTransitiveRepositories(); + + /** + * Specifies "@file"-like file, to load up command line from. It may contain goals as well. Format is one parameter + * per line (similar to {@code maven.conf}) and {@code '#'} (hash) marked comment lines are allowed. Goals, if + * present, are appended, to those specified on CLI input, if any. + */ + Optional atFile(); + + /** + * Returns the list of goals and phases to execute. + * + * @return an {@link Optional} containing the list of goals and phases to execute, or empty if not specified + */ + @Nonnull + Optional> goals(); +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/package-info.java new file mode 100644 index 000000000000..dd191227d125 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/package-info.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides the API for the Maven build tool ({@code mvn}). + * + *

This package contains interfaces and classes specific to the main Maven build + * tool, which is responsible for project build lifecycle execution and dependency management.

+ * + *

Key features include:

+ *
    + *
  • Build lifecycle execution control
  • + *
  • Project-specific configuration
  • + *
  • Goal execution and phase mapping
  • + *
  • Multi-module build coordination
  • + *
+ * + * @see org.apache.maven.api.cli.Tools#MVN_CMD + * @see org.apache.maven.api.cli.Tools#MVN_NAME + * @since 4.0.0 + */ +package org.apache.maven.api.cli.mvn; diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptOptions.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptOptions.java new file mode 100644 index 000000000000..f2f38f3cb0c5 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptOptions.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli.mvnenc; + +import java.util.List; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.cli.Options; + +/** + * Defines the options specific to the Maven encryption tool. + * This interface extends the general {@link Options} interface, adding encryption-specific configuration options. + * + * @since 4.0.0 + */ +@Experimental +public interface EncryptOptions extends Options { + /** + * Should the operation be forced (ie overwrite existing config, if any). + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + Optional force(); + + /** + * Should imply "yes" to all questions. + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + Optional yes(); + + /** + * Returns the list of encryption goals to be executed. + * These goals can include operations like "init", "add-server", "delete-server", etc. + * + * @return an {@link Optional} containing the list of goals, or empty if not specified + */ + @Nonnull + Optional> goals(); +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/package-info.java new file mode 100644 index 000000000000..992b6aa38a55 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/package-info.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides the API for the Maven Password Encryption tool ({@code mvnenc}). + * + *

This package contains interfaces and classes for the password encryption tool, + * which helps secure sensitive information in Maven settings and configuration files.

+ * + *

Key features include:

+ *
    + *
  • Password encryption and decryption
  • + *
  • Master password management
  • + *
  • Security settings configuration
  • + *
+ * + * @see org.apache.maven.api.cli.Tools#MVNENC_CMD + * @see org.apache.maven.api.cli.Tools#MVNENC_NAME + * @since 4.0.0 + */ +package org.apache.maven.api.cli.mvnenc; diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnsh/ShellOptions.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnsh/ShellOptions.java new file mode 100644 index 000000000000..05abbd804f34 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnsh/ShellOptions.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli.mvnsh; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.cli.Options; + +/** + * Defines the options specific to the Maven Shell tool. + * This interface extends the general {@link Options} interface, adding shell-specific configuration options. + * + * @since 4.0.0 + */ +@Experimental +public interface ShellOptions extends Options {} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnsh/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnsh/package-info.java new file mode 100644 index 000000000000..571925645555 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnsh/package-info.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides the API for the Maven Shell tool ({@code mvnsh}). + * + *

This package contains interfaces and classes for the interactive Maven shell, + * which provides a command-line interface for executing Maven commands and + * managing build environments.

+ * + *

Key features include:

+ *
    + *
  • Interactive command-line interface
  • + *
  • Command history and completion
  • + *
  • Built-in shell commands
  • + *
  • Project navigation and management
  • + *
+ * + * @see org.apache.maven.api.cli.Tools#MVNSHELL_CMD + * @see org.apache.maven.api.cli.Tools#MVNSHELL_NAME + * @since 4.0.0 + */ +package org.apache.maven.api.cli.mvnsh; diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnup/UpgradeOptions.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnup/UpgradeOptions.java new file mode 100644 index 000000000000..47d8a67cbec1 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnup/UpgradeOptions.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cli.mvnup; + +import java.util.List; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.cli.Options; + +/** + * Defines the options specific to the Maven upgrade tool. + * This interface extends the general {@link Options} interface, adding upgrade-specific configuration options. + * + * @since 4.0.0 + */ +@Experimental +public interface UpgradeOptions extends Options { + /** + * Should the operation be forced (ie overwrite existing files, if any). + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + Optional force(); + + /** + * Should imply "yes" to all questions. + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + Optional yes(); + + /** + * Returns the list of upgrade goals to be executed. + * These goals can include operations like "check", "dependencies", "plugins", etc. + * + * @return an {@link Optional} containing the list of goals, or empty if not specified + */ + @Nonnull + Optional> goals(); + + /** + * Returns the target POM model version for upgrades. + * Supported values include "4.0.0" and "4.1.0". + * + * @return an {@link Optional} containing the model version, or empty if not specified + */ + @Nonnull + Optional modelVersion(); + + /** + * Returns the directory to use as starting point for POM discovery. + * If not specified, the current directory will be used. + * + * @return an {@link Optional} containing the directory path, or empty if not specified + */ + @Nonnull + Optional directory(); + + /** + * Should use inference when upgrading (remove redundant information). + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + @Nonnull + Optional infer(); + + /** + * Should fix Maven 4 compatibility issues in POMs. + * This includes fixing unsupported combine attributes, duplicate dependencies, + * unsupported expressions, and other Maven 4 validation issues. + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + @Nonnull + Optional model(); + + /** + * Should upgrade plugins known to fail with Maven 4 to their minimum compatible versions. + * This includes upgrading plugins like maven-exec-plugin, maven-enforcer-plugin, + * flatten-maven-plugin, and maven-shade-plugin to versions that work with Maven 4. + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + @Nonnull + Optional plugins(); + + /** + * Should apply all upgrade options (equivalent to --model-version 4.1.0 --infer --model --plugins). + * This is a convenience option that combines model upgrade, inference, compatibility fixes, and plugin upgrades. + * + * @return an {@link Optional} containing the boolean value {@code true} if specified, or empty + */ + @Nonnull + Optional all(); +} diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnup/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnup/package-info.java new file mode 100644 index 000000000000..d260dcffb3f6 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnup/package-info.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides the API for the Maven Upgrade tool ({@code mvnup}). + * + *

This package contains interfaces and classes for the Maven upgrade tool, + * which provides functionality for upgrading Maven projects and dependencies.

+ * + *

Key features include:

+ *
    + *
  • Project upgrade capabilities
  • + *
  • Dependency version management
  • + *
  • Configuration migration
  • + *
  • Interactive upgrade workflows
  • + *
+ * + * @see org.apache.maven.api.cli.Tools#MVNUP_CMD + * @see org.apache.maven.api.cli.Tools#MVNUP_NAME + * @since 4.0.0 + */ +package org.apache.maven.api.cli.mvnup; diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/package-info.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/package-info.java new file mode 100644 index 000000000000..c94bbe0a1006 --- /dev/null +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/package-info.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides the API for Maven's command-line interface and tools. + * + *

This package contains interfaces and classes for:

+ *
    + *
  • Command-line argument parsing and processing
  • + *
  • Maven tool invocation ({@code mvn}, {@code mvnenc}, {@code mvnsh})
  • + *
  • Core extensions configuration
  • + *
  • Early-stage logging before the full Maven logging system is initialized
  • + *
+ * + *

The main components are:

+ *
    + *
  • {@link org.apache.maven.api.cli.Invoker} - Base interface for executing Maven tools
  • + *
  • {@link org.apache.maven.api.cli.Parser} - Processes command-line arguments into invoker requests
  • + *
  • {@link org.apache.maven.api.cli.Options} - Represents Maven configuration options
  • + *
  • {@link org.apache.maven.api.cli.extensions.CoreExtensions} - Manages Maven core extensions
  • + *
+ * + *

Core extensions can be configured through {@code .mvn/extensions.xml} in the project base directory + * to enhance Maven's capabilities during build execution.

+ * + * @since 4.0.0 + */ +package org.apache.maven.api.cli; diff --git a/api/maven-api-cli/src/main/mdo/core-extensions.mdo b/api/maven-api-cli/src/main/mdo/core-extensions.mdo new file mode 100644 index 000000000000..8bbcd6cb3365 --- /dev/null +++ b/api/maven-api-cli/src/main/mdo/core-extensions.mdo @@ -0,0 +1,121 @@ + + + + + + + core-extensions + CoreExtensions + This is a reference for the Core Extensions descriptor.

+

The default location for the Core Extensions descriptor file is ${maven.projectBasedir}/.mvn/extensions.xml

+ ]]>
+ + + + package + org.apache.maven.cli.internal.extension.model + + + + + + CoreExtensions + Extensions to load. + 1.0.0+ + + + extensions + A set of build extensions to use from this project. + 1.0.0+ + + CoreExtension + * + + + + + + CoreExtension + Describes a build extension to utilise. + 1.0.0+ + + + groupId + The group ID of the extension's artifact. + 1.0.0+ + true + String + + + artifactId + The artifact ID of the extension. + 1.0.0+ + true + String + + + version + The version of the extension. + 1.0.0+ + true + String + + + classLoadingStrategy + The class loading strategy: 'self-first' (the default), 'parent-first' (loads classes from the parent, then from the extension) or 'plugin' (follows the rules from extensions defined as plugins). + 1.1.0+ + self-first + false + String + + + configuration + 1.2.0+ + false + DOM + + + + + 1.0.0+ + + ::}, never {@code null}. + */ + public String getId() { + return (getGroupId() == null ? "[unknown-group-id]" : getGroupId()) + + ":" + (getArtifactId() == null ? "[unknown-artifact-id]" : getArtifactId()) + + ":" + (getVersion() == null ? "[unknown-version]" : getVersion()); + } + ]]> + + + + + +
diff --git a/api/maven-api-core/pom.xml b/api/maven-api-core/pom.xml new file mode 100644 index 000000000000..339ea8319217 --- /dev/null +++ b/api/maven-api-core/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-core + Maven 4 API :: Core + Maven 4 API - Maven Core API + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-di + + + org.apache.maven + maven-api-model + + + org.apache.maven + maven-api-settings + + + org.apache.maven + maven-api-toolchain + + + org.apache.maven + maven-api-plugin + + + org.apache.maven + maven-api-xml + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + + diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java new file mode 100644 index 000000000000..d26972c0060b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + * A Maven artifact is a file, typically a JAR, that is produced and used by Maven projects. + * It is identified by a unique combination of a group ID, artifact ID, version, classifier, + * and extension, and it is stored in a repository for dependency management and build purposes. + * + *

Each {@code Artifact} instance is basically an exact pointer to a file in a Maven repository. + * {@code Artifact} instances are created when resolving {@link ArtifactCoordinates} instances. + * Resolving is the process that selects a {@linkplain #getVersion() particular version} + * and downloads the artifact in the local repository. This operation returns a {@link DownloadedArtifact}. + *

+ * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Artifact { + /** + * {@return a unique identifier for this artifact}. + * The identifier is composed of groupId, artifactId, extension, classifier, and version. + * + * @see ArtifactCoordinates#getId() + */ + @Nonnull + default String key() { + String c = getClassifier(); + return getGroupId() + + ':' + + getArtifactId() + + ':' + + getExtension() + + (c.isEmpty() ? "" : ":" + c) + + ':' + + getVersion(); + } + + /** + * {@return the group identifier of the artifact}. + * + * @see ArtifactCoordinates#getGroupId() + */ + @Nonnull + String getGroupId(); + + /** + * {@return the identifier of the artifact}. + * + * @see ArtifactCoordinates#getArtifactId() + */ + @Nonnull + String getArtifactId(); + + /** + * {@return the version of the artifact}. + * Contrarily to {@link ArtifactCoordinates}, + * each {@code Artifact} is associated to a specific version instead of a range of versions. + * If the {@linkplain #getBaseVersion() base version} contains a meta-version such as {@code SNAPSHOT}, + * those keywords are replaced by, for example, the actual timestamp. + * + * @see ArtifactCoordinates#getVersionConstraint() + */ + @Nonnull + Version getVersion(); + + /** + * {@return the version or meta-version of the artifact}. + * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. + * Meta-versions are represented in a base version by their symbols (e.g., {@code SNAPSHOT}), + * while they are replaced by, for example, the actual timestamp in the {@linkplain #getVersion() version}. + */ + @Nonnull + Version getBaseVersion(); + + /** + * Returns the classifier of the artifact. + * + * @return the classifier or an empty string if none, never {@code null} + * @see ArtifactCoordinates#getClassifier() + */ + @Nonnull + String getClassifier(); + + /** + * Returns the file extension of the artifact. + * The dot separator is not included in the returned string. + * + * @return the file extension or an empty string if none, never {@code null} + * @see ArtifactCoordinates#getExtension() + */ + @Nonnull + String getExtension(); + + /** + * Determines whether this artifact uses a snapshot version. + * + * @return {@code true} if the artifact is a snapshot, {@code false} otherwise + * @see org.apache.maven.api.Session#isVersionSnapshot(String) + */ + boolean isSnapshot(); + + /** + * {@return coordinates with the same identifiers as this artifact}. + * This is a shortcut for {@code session.createArtifactCoordinates(artifact)}. + * + * @see org.apache.maven.api.Session#createArtifactCoordinates(Artifact) + */ + @Nonnull + ArtifactCoordinates toCoordinates(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java new file mode 100644 index 000000000000..89a7e37aa581 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Partial identification of an {@link Artifact} in a Maven repository. + * Each {@code ArtifactCoordinates} instance is basically a pointer to a file in the Maven repository, + * except that the exact version may not be known yet. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ArtifactCoordinates { + /** + * {@return the group identifier of the artifact}. + */ + @Nonnull + String getGroupId(); + + /** + * {@return the identifier of the artifact}. + */ + @Nonnull + String getArtifactId(); + + /** + * Returns the classifier of the artifact. + * + * @return the classifier or an empty string if none, never {@code null} + */ + @Nonnull + String getClassifier(); + + /** + * {@return the specific version, range of versions, or meta-version of the artifact}. + * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. + */ + @Nonnull + VersionConstraint getVersionConstraint(); + + /** + * Returns the file extension of the artifact. + * The dot separator is not included in the returned string. + * + * @return the file extension or an empty string if none, never {@code null} + */ + @Nonnull + String getExtension(); + + /** + * {@return a unique string identifying this artifact}. + * + * The default implementation returns a colon-separated list of group + * identifier, artifact identifier, extension, classifier and version. + * + * @see Artifact#key() + */ + @Nonnull + default String getId() { + String c = getClassifier(); + return getGroupId() + + ':' + + getArtifactId() + + ':' + + getExtension() + + ':' + + c + + (c.isEmpty() ? "" : ":") + + getVersionConstraint(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java new file mode 100644 index 000000000000..e9cba7a434a0 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java @@ -0,0 +1,666 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Config; + +/** + * Configuration constants. + */ +public final class Constants { + + /** + * Maven home. + * + * @since 3.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_HOME = "maven.home"; + + /** + * Maven version. + * + * @since 3.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_VERSION = "maven.version"; + + /** + * Maven major version: contains the major segment of this Maven version. + * + * @since 4.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_VERSION_MAJOR = "maven.version.major"; + + /** + * Maven minor version: contains the minor segment of this Maven version. + * + * @since 4.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_VERSION_MINOR = "maven.version.minor"; + + /** + * Maven patch version: contains the patch segment of this Maven version. + * + * @since 4.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_VERSION_PATCH = "maven.version.patch"; + + /** + * Maven snapshot: contains "true" if this Maven is a snapshot version. + * + * @since 4.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_VERSION_SNAPSHOT = "maven.version.snapshot"; + + /** + * Maven build version: a human-readable string containing this Maven version, buildnumber, and time of its build. + * + * @since 3.0.0 + */ + @Config(readOnly = true, source = Config.Source.SYSTEM_PROPERTIES) + public static final String MAVEN_BUILD_VERSION = "maven.build.version"; + + /** + * Maven installation configuration directory. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.home}/conf") + public static final String MAVEN_INSTALLATION_CONF = "maven.installation.conf"; + + /** + * Maven user configuration directory. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${user.home}/.m2") + public static final String MAVEN_USER_CONF = "maven.user.conf"; + + /** + * Maven project configuration directory. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${session.rootDirectory}/.mvn") + public static final String MAVEN_PROJECT_CONF = "maven.project.conf"; + + /** + * Maven local repository. + * + * @since 3.0.0 + */ + @Config(defaultValue = "${maven.user.conf}/repository") + public static final String MAVEN_REPO_LOCAL = "maven.repo.local"; + + /** + * Maven central repository URL. + * The property will have the value of the MAVEN_REPO_CENTRAL + * environment variable if it is defined. + * + * @since 4.0.0 + */ + @Config(defaultValue = "https://repo.maven.apache.org/maven2") + public static final String MAVEN_REPO_CENTRAL = "maven.repo.central"; + + /** + * Maven installation settings. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.installation.conf}/settings.xml") + public static final String MAVEN_INSTALLATION_SETTINGS = "maven.installation.settings"; + + /** + * Maven user settings. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.user.conf}/settings.xml") + public static final String MAVEN_USER_SETTINGS = "maven.user.settings"; + + /** + * Maven project settings. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.project.conf}/settings.xml") + public static final String MAVEN_PROJECT_SETTINGS = "maven.project.settings"; + + /** + * Maven installation extensions. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.installation.conf}/extensions.xml") + public static final String MAVEN_INSTALLATION_EXTENSIONS = "maven.installation.extensions"; + + /** + * Maven user extensions. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.user.conf}/extensions.xml") + public static final String MAVEN_USER_EXTENSIONS = "maven.user.extensions"; + + /** + * Maven project extensions. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.project.conf}/extensions.xml") + public static final String MAVEN_PROJECT_EXTENSIONS = "maven.project.extensions"; + + /** + * Maven installation toolchains. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.installation.conf}/toolchains.xml") + public static final String MAVEN_INSTALLATION_TOOLCHAINS = "maven.installation.toolchains"; + + /** + * Maven user toolchains. + * + * @since 4.0.0 + */ + @Config(defaultValue = "${maven.user.conf}/toolchains.xml") + public static final String MAVEN_USER_TOOLCHAINS = "maven.user.toolchains"; + + /** + * Extensions class path. + */ + @Config + public static final String MAVEN_EXT_CLASS_PATH = "maven.ext.class.path"; + + @Config(defaultValue = "${maven.user.conf}/settings-security4.xml") + public static final String MAVEN_SETTINGS_SECURITY = "maven.settings.security"; + + public static final String MAVEN_SETTINGS_SECURITY_FILE_NAME = "settings-security4.xml"; + + public static final String MAVEN_STYLE_PREFIX = "maven.style."; + + // Style Names + public static final String MAVEN_STYLE_TRANSFER_NAME = "transfer"; + public static final String MAVEN_STYLE_TRACE_NAME = "trace"; + public static final String MAVEN_STYLE_DEBUG_NAME = "debug"; + public static final String MAVEN_STYLE_INFO_NAME = "info"; + public static final String MAVEN_STYLE_WARNING_NAME = "warning"; + public static final String MAVEN_STYLE_ERROR_NAME = "error"; + public static final String MAVEN_STYLE_SUCCESS_NAME = "success"; + public static final String MAVEN_STYLE_FAILURE_NAME = "failure"; + public static final String MAVEN_STYLE_STRONG_NAME = "strong"; + public static final String MAVEN_STYLE_MOJO_NAME = "mojo"; + public static final String MAVEN_STYLE_PROJECT_NAME = "project"; + + // Default Values + public static final String MAVEN_STYLE_TRANSFER_DEFAULT = "f:bright-black"; + public static final String MAVEN_STYLE_TRACE_DEFAULT = "bold,f:magenta"; + public static final String MAVEN_STYLE_DEBUG_DEFAULT = "bold,f:cyan"; + public static final String MAVEN_STYLE_INFO_DEFAULT = "bold,f:blue"; + public static final String MAVEN_STYLE_WARNING_DEFAULT = "bold,f:yellow"; + public static final String MAVEN_STYLE_ERROR_DEFAULT = "bold,f:red"; + public static final String MAVEN_STYLE_SUCCESS_DEFAULT = "bold,f:green"; + public static final String MAVEN_STYLE_FAILURE_DEFAULT = "bold,f:red"; + public static final String MAVEN_STYLE_STRONG_DEFAULT = "bold"; + public static final String MAVEN_STYLE_MOJO_DEFAULT = "f:green"; + public static final String MAVEN_STYLE_PROJECT_DEFAULT = "f:cyan"; + + /** + * Maven output color mode. + * Allowed values are auto, always, never. + * + * @since 4.0.0 + */ + @Config(defaultValue = "auto") + public static final String MAVEN_STYLE_COLOR_PROPERTY = MAVEN_STYLE_PREFIX + "color"; + + /** + * Color style for transfer messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_TRANSFER_DEFAULT) + public static final String MAVEN_STYLE_TRANSFER = MAVEN_STYLE_PREFIX + MAVEN_STYLE_TRANSFER_NAME; + + /** + * Color style for trace messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_TRACE_DEFAULT) + public static final String MAVEN_STYLE_TRACE = MAVEN_STYLE_PREFIX + MAVEN_STYLE_TRACE_NAME; + + /** + * Color style for debug messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_DEBUG_DEFAULT) + public static final String MAVEN_STYLE_DEBUG = MAVEN_STYLE_PREFIX + MAVEN_STYLE_DEBUG_NAME; + + /** + * Color style for info messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_INFO_DEFAULT) + public static final String MAVEN_STYLE_INFO = MAVEN_STYLE_PREFIX + MAVEN_STYLE_INFO_NAME; + + /** + * Color style for warning messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_WARNING_DEFAULT) + public static final String MAVEN_STYLE_WARNING = MAVEN_STYLE_PREFIX + MAVEN_STYLE_WARNING_NAME; + + /** + * Color style for error messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_ERROR_DEFAULT) + public static final String MAVEN_STYLE_ERROR = MAVEN_STYLE_PREFIX + MAVEN_STYLE_ERROR_NAME; + + /** + * Color style for success messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_SUCCESS_DEFAULT) + public static final String MAVEN_STYLE_SUCCESS = MAVEN_STYLE_PREFIX + MAVEN_STYLE_SUCCESS_NAME; + + /** + * Color style for failure messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_FAILURE_DEFAULT) + public static final String MAVEN_STYLE_FAILURE = MAVEN_STYLE_PREFIX + MAVEN_STYLE_FAILURE_NAME; + + /** + * Color style for strong messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_STRONG_DEFAULT) + public static final String MAVEN_STYLE_STRONG = MAVEN_STYLE_PREFIX + MAVEN_STYLE_STRONG_NAME; + + /** + * Color style for mojo messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_MOJO_DEFAULT) + public static final String MAVEN_STYLE_MOJO = MAVEN_STYLE_PREFIX + MAVEN_STYLE_MOJO_NAME; + + /** + * Color style for project messages. + * @since 4.0.0 + */ + @Config(defaultValue = MAVEN_STYLE_PROJECT_DEFAULT) + public static final String MAVEN_STYLE_PROJECT = MAVEN_STYLE_PREFIX + MAVEN_STYLE_PROJECT_NAME; + + /** + * Build timestamp format. + * + * @since 3.0.0 + */ + @Config(source = Config.Source.MODEL, defaultValue = "yyyy-MM-dd'T'HH:mm:ssXXX") + public static final String MAVEN_BUILD_TIMESTAMP_FORMAT = "maven.build.timestamp.format"; + + /** + * User controlled relocations. + * This property is a comma separated list of entries with the syntax GAV>GAV. + * The first GAV can contain * for any elem (so *:*:* would mean ALL, something + * you don't want). The second GAV is either fully specified, or also can contain *, + * then it behaves as "ordinary relocation": the coordinate is preserved from relocated artifact. + * Finally, if right hand GAV is absent (line looks like GAV>), the left hand matching + * GAV is banned fully (from resolving). + *
+ * Note: the > means project level, while >> means global (whole session level, + * so even plugins will get relocated artifacts) relocation. + *
+ * For example, + *
maven.relocations.entries = org.foo:*:*>, \\
org.here:*:*>org.there:*:*, \\
javax.inject:javax.inject:1>>jakarta.inject:jakarta.inject:1.0.5
+ * means: 3 entries, ban org.foo group (exactly, so org.foo.bar is allowed), + * relocate org.here to org.there and finally globally relocate (see >> above) + * javax.inject:javax.inject:1 to jakarta.inject:jakarta.inject:1.0.5. + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_RELOCATIONS_ENTRIES = "maven.relocations.entries"; + + /** + * User property for version filter expression used in session, applied to resolving ranges: a semicolon separated + * list of filters to apply. By default, no version filter is applied (like in Maven 3). + *
+ * Supported filters: + *
    + *
  • "h" or "h(num)" - highest version or top list of highest ones filter
  • + *
  • "l" or "l(num)" - lowest version or bottom list of lowest ones filter
  • + *
  • "s" - contextual snapshot filter
  • + *
  • "ns" - unconditional snapshot filter (no snapshots selected from ranges)
  • + *
  • "e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)
  • + *
+ * Example filter expression: "h(5);s;e(org.foo:bar:1) will cause: ranges are filtered for "top 5" (instead + * full range), snapshots are banned if root project is not a snapshot, and if range for org.foo:bar is + * being processed, version 1 is omitted. Value in this property builds + * org.eclipse.aether.collection.VersionFilter instance. + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_VERSION_FILTER = "maven.session.versionFilter"; + + /** + * User property for chained LRM: the new "head" local repository to use, and "push" the existing into tail. + * Similar to maven.repo.local.tail, this property may contain comma separated list of paths to be + * used as local repositories (combine with chained local repository), but while latter is "appending" this + * one is "prepending". + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_REPO_LOCAL_HEAD = "maven.repo.local.head"; + + /** + * User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with + * org.eclipse.aether.util.repository.ChainedLocalRepositoryManager. + * Default value: null, no chained LRM is used. + * + * @since 3.9.0 + */ + @Config + public static final String MAVEN_REPO_LOCAL_TAIL = "maven.repo.local.tail"; + + /** + * User property for chained LRM: whether to ignore "availability check" in tail or not. Usually you do want + * to ignore it. This property is mapped onto corresponding Resolver 2.x property, is like a synonym for it. + * Default value: true. + * + * @since 3.9.0 + * @see Resolver Configuration: aether.chainedLocalRepository.ignoreTailAvailability + */ + @Config + public static final String MAVEN_REPO_LOCAL_TAIL_IGNORE_AVAILABILITY = "maven.repo.local.tail.ignoreAvailability"; + + /** + * User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local + * repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local + * repository. + * Default: false, will not record anything. + * + * @since 3.9.0 + */ + @Config(defaultValue = "false") + public static final String MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE = "maven.repo.local.recordReverseTree"; + + /** + * User property for selecting dependency manager behaviour regarding transitive dependencies and dependency + * management entries in their POMs. Maven 3 targeted full backward compatibility with Maven 2. Hence, it ignored + * dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default. Hence + * unlike Maven 3, it obeys dependency management entries deep in the dependency graph as well. + *
+ * Default: "true". + * + * @since 4.0.0 + */ + @Config(defaultValue = "true") + public static final String MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY = + "maven.resolver.dependencyManagerTransitivity"; + + /** + * Resolver transport to use. + * Can be default, wagon, apache, jdk or auto. + * + * @since 4.0.0 + */ + @Config(defaultValue = "default") + public static final String MAVEN_RESOLVER_TRANSPORT = "maven.resolver.transport"; + + /** + * Plugin validation level. + * + * @since 3.9.2 + */ + @Config(defaultValue = "inline") + public static final String MAVEN_PLUGIN_VALIDATION = "maven.plugin.validation"; + + /** + * Plugin validation exclusions. + * + * @since 3.9.6 + */ + @Config + public static final String MAVEN_PLUGIN_VALIDATION_EXCLUDES = "maven.plugin.validation.excludes"; + + /** + * ProjectBuilder parallelism. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Integer", defaultValue = "cores/2 + 1") + public static final String MAVEN_MODEL_BUILDER_PARALLELISM = "maven.modelBuilder.parallelism"; + + /** + * User property for enabling/disabling the consumer POM feature. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "true") + public static final String MAVEN_CONSUMER_POM = "maven.consumer.pom"; + + /** + * User property for controlling "maven personality". If activated Maven will behave + * like the previous major version, Maven 3. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_MAVEN3_PERSONALITY = "maven.maven3Personality"; + + /** + * User property for disabling version resolver cache. + * + * @since 3.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_VERSION_RESOLVER_NO_CACHE = "maven.versionResolver.noCache"; + + /** + * User property for overriding calculated "build number" for snapshot deploys. Caution: this property should + * be RARELY used (if used at all). It may help in special cases like "aligning" a reactor build subprojects + * build numbers to perform a "snapshot lock down". Value given here must be maxRemoteBuildNumber + 1 + * or greater, otherwise build will fail. How the number to be obtained is left to user (ie by inspecting + * snapshot repository metadata or alike). + * + * Note: this feature is present in Maven 3.9.7 but with different key: maven.buildNumber. In Maven 4 + * as part of cleanup effort this key was renamed to properly reflect its purpose. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Integer") + public static final String MAVEN_DEPLOY_SNAPSHOT_BUILD_NUMBER = "maven.deploy.snapshot.buildNumber"; + + /** + * User property for controlling whether build POMs are deployed alongside consumer POMs. + * When set to false, only the consumer POM will be deployed, and the build POM + * will be excluded from deployment. This is useful to avoid deploying internal build information + * that is not needed by consumers of the artifact. + *
+ * Default: "true". + * + * @since 4.1.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "true") + public static final String MAVEN_DEPLOY_BUILD_POM = "maven.deploy.buildPom"; + + /** + * User property used to store the build timestamp. + * + * @since 4.0.0 + */ + @Config(type = "java.time.Instant") + public static final String MAVEN_START_INSTANT = "maven.startInstant"; + + /** + * Max number of problems for each severity level retained by the model builder. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Integer", defaultValue = "100") + public static final String MAVEN_BUILDER_MAX_PROBLEMS = "maven.builder.maxProblems"; + + /** + * Configuration property for version range resolution used metadata "nature". + * It may contain following string values: + *
    + *
  • "auto" - decision done based on range being resolver: if any boundary is snapshot, use "release_or_snapshot", otherwise "release"
  • + *
  • "release_or_snapshot" - the default
  • + *
  • "release" - query only release repositories to discover versions
  • + *
  • "snapshot" - query only snapshot repositories to discover versions
  • + *
+ * Default (when unset) is existing Maven behaviour: "release_or_snapshots". + * @since 4.0.0 + */ + @Config(defaultValue = "release_or_snapshot") + public static final String MAVEN_VERSION_RANGE_RESOLVER_NATURE_OVERRIDE = + "maven.versionRangeResolver.natureOverride"; + + /** + * Comma-separated list of XML contexts/fields to intern during POM parsing for memory optimization. + * When not specified, a default set of commonly repeated contexts will be used. + * Example: "groupId,artifactId,version,scope,type" + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_MODEL_BUILDER_INTERNS = "maven.modelBuilder.interns"; + + /** + * All system properties used by Maven Logger start with this prefix. + * + * @since 4.0.0 + */ + public static final String MAVEN_LOGGER_PREFIX = "maven.logger."; + + /** + * Default log level for all instances of SimpleLogger. Must be one of ("trace", "debug", "info", + * "warn", "error" or "off"). If not specified, defaults to "info". + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_LOGGER_DEFAULT_LOG_LEVEL = MAVEN_LOGGER_PREFIX + "defaultLogLevel"; + + /** + * Set to true if you want the current date and time to be included in output messages. Default is false. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_LOGGER_SHOW_DATE_TIME = MAVEN_LOGGER_PREFIX + "showDateTime"; + + /** + * The date and time format to be used in the output messages. The pattern describing the date and + * time format is defined by SimpleDateFormat. If the format is not specified or is invalid, the + * number of milliseconds since start up will be output. + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_LOGGER_DATE_TIME_FORMAT = MAVEN_LOGGER_PREFIX + "dateTimeFormat"; + + /** + * If you would like to output the current thread id, then set to true. Defaults to false. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_LOGGER_SHOW_THREAD_ID = MAVEN_LOGGER_PREFIX + "showThreadId"; + + /** + * Set to true if you want to output the current thread name. Defaults to true. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "true") + public static final String MAVEN_LOGGER_SHOW_THREAD_NAME = MAVEN_LOGGER_PREFIX + "showThreadName"; + + /** + * Set to true if you want the Logger instance name to be included in output messages. Defaults to true. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "true") + public static final String MAVEN_LOGGER_SHOW_LOG_NAME = MAVEN_LOGGER_PREFIX + "showLogName"; + + /** + * Set to true if you want the last component of the name to be included in output messages. Defaults to false. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_LOGGER_SHOW_SHORT_LOG_NAME = MAVEN_LOGGER_PREFIX + "showShortLogName"; + + /** + * The output target which can be the path to a file, or the special values "System.out" and "System.err". + * Default is "System.err". + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_LOGGER_LOG_FILE = MAVEN_LOGGER_PREFIX + "logFile"; + + /** + * Should the level string be output in brackets? Defaults to false. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_LOGGER_LEVEL_IN_BRACKETS = MAVEN_LOGGER_PREFIX + "levelInBrackets"; + + /** + * The string value output for the warn level. Defaults to WARN. + * + * @since 4.0.0 + */ + @Config(defaultValue = "WARN") + public static final String MAVEN_LOGGER_WARN_LEVEL = MAVEN_LOGGER_PREFIX + "warnLevelString"; + + /** + * If the output target is set to "System.out" or "System.err" (see preceding entry), by default, logs will + * be output to the latest value referenced by System.out/err variables. By setting this parameter to true, + * the output stream will be cached, i.e. assigned once at initialization time and re-used independently of + * the current value referenced by System.out/err. + * + * @since 4.0.0 + */ + @Config(type = "java.lang.Boolean", defaultValue = "false") + public static final String MAVEN_LOGGER_CACHE_OUTPUT_STREAM = MAVEN_LOGGER_PREFIX + "cacheOutputStream"; + + /** + * maven.logger.log.a.b.c - Logging detail level for a SimpleLogger instance named "a.b.c". Right-side value + * must be one of "trace", "debug", "info", "warn", "error" or "off". When a logger named "a.b.c" is initialized, + * its level is assigned from this property. If unspecified, the level of nearest parent logger will be used, + * and if none is set, then the value specified by {@code maven.logger.defaultLogLevel} will be used. + * + * @since 4.0.0 + */ + public static final String MAVEN_LOGGER_LOG_PREFIX = MAVEN_LOGGER_PREFIX + "log."; + + private Constants() {} +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java new file mode 100644 index 000000000000..95dc5d689efa --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + * A result of collecting, flattening, and resolving {@link DependencyCoordinates}s. + * Dependency is the output of the collection process, which builds the graph of dependencies, + * followed by flattening and resolution. + * The version selection is done for each dependency during the collection phase. + * The flatten phase keeps only a single version per ({@code groupId}, {@code artifactId}) pair. + * The resolution phase actually downloads the dependencies (or artifacts) that have been computed. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Dependency extends Artifact { + /** + * {@return the type of the dependency}. + * A dependency can be a JAR file, + * a modular-JAR if it is intended to be placed on the module path, + * a JAR containing test classes, etc. + * + * @see DependencyCoordinates#getType() + */ + @Nonnull + Type getType(); + + /** + * {@return the time at which the dependency will be used}. + * It may be, for example, at compile time only, at run time or at test time. + * + * @see DependencyCoordinates#getScope() + */ + @Nonnull + DependencyScope getScope(); + + /** + * Returns whether the dependency is optional or mandatory. + * Unlike {@link DependencyCoordinates}, the obligation of a {@code Dependency} is always present. + * The value is computed during the dependencies collection phase. + * + * @return {@code true} if the dependency is optional, or {@code false} if mandatory + * @see DependencyCoordinates#getOptional() + */ + boolean isOptional(); + + /** + * {@return coordinates with the same identifiers as this dependency}. + */ + @Nonnull + @Override + DependencyCoordinates toCoordinates(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java new file mode 100644 index 000000000000..cbcc264db653 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Collection; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * {@code ArtifactCoordinates} completed with information about how the artifact will be used. + * This information includes the dependency type (main classes, test classes, etc.), + * a scope (compile, runtime etc.), an obligation (whether the dependency + * is optional or mandatory), and possible exclusions for transitive dependencies. + * The {@linkplain #getVersionConstraint() version} and the {@linkplain #getOptional() obligation} + * may not be defined precisely. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface DependencyCoordinates extends ArtifactCoordinates { + /** + * {@return the type of the dependency} + * A dependency can be a JAR file, + * a modular-JAR if it is intended to be placed on the module path, + * a JAR containing test classes, a POM file, etc. + */ + @Nonnull + Type getType(); + + /** + * {@return the time at which the dependency will be used} + * It may be, for example, at compile time only, at run time, or at test time. + */ + @Nonnull + DependencyScope getScope(); + + /** + * Returns whether the dependency is optional, mandatory, or of unspecified obligation. + * + * @return {@code Boolean.TRUE} and {@code Boolean.FALSE} if optional, or {@code null} if unspecified + */ + @Nullable + Boolean getOptional(); + + /** + * {@return transitive dependencies to exclude} + */ + @Nonnull + Collection getExclusions(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyScope.java new file mode 100644 index 000000000000..f9f788e00316 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyScope.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Indicates when the dependency will be used. + * For example, it may be at compile time only, at runtime, or at test time. + * For a given dependency, the scope is directly derived from the + * {@link org.apache.maven.api.model.Dependency#getScope()} and will be used when using {@link PathScope} + * and the {@link org.apache.maven.api.services.DependencyResolver}. + * + * @since 4.0.0 + * @see org.apache.maven.api.model.Dependency#getScope() + * @see org.apache.maven.api.services.DependencyResolver + */ +@Experimental +@Immutable +public enum DependencyScope { + + /** + * None. Allows you to declare dependencies (for example to alter reactor build order) but in reality dependencies + * in this scope are not part of any path scope. + */ + NONE("none", false), + + /** + * Undefined. When no scope is explicitly given, UNDEFINED will be used, but its meaning will depend on + * whether the DependencyCoordinates is used in dependency management, in which case it means the scope is not + * explicitly managed by this managed dependency, or as a real dependency, in which case, the scope + * will default to {@link #COMPILE}. + */ + UNDEFINED("", false), + + /** + * Compile only. + */ + COMPILE_ONLY("compile-only", false), + + /** + * Compile, runtime and test. + */ + COMPILE("compile", true), + + /** + * Runtime and test. + */ + RUNTIME("runtime", true), + + /** + * Provided. + */ + PROVIDED("provided", false), + + /** + * Test compile only. + */ + TEST_ONLY("test-only", false), + + /** + * Test compile and test runtime. + */ + TEST("test", false), + + /** + * Test runtime. + */ + TEST_RUNTIME("test-runtime", false), + + /** + * System scope. + */ + SYSTEM("system", false); + + private static final Map IDS = Collections.unmodifiableMap( + Stream.of(DependencyScope.values()).collect(Collectors.toMap(s -> s.id, s -> s))); + + /** + * {@return the dependency scope for the given identifier, or {@code null} if none}. + * The identifiers are usually in lower cases with {@code '-'} instead of {@code '_'} + * as word separator. + * + * @param id the identifier of the scope (case-sensitive) + */ + public static DependencyScope forId(String id) { + return IDS.get(id); + } + + private final String id; + private final boolean transitive; + + DependencyScope(String id, boolean transitive) { + this.id = id; + this.transitive = transitive; + } + + /** + * The {@code id} uniquely represents a value for this extensible enum. + * This id should be used to compute the equality and hash code for the instance. + * + * @return the id + */ + @Nonnull + public String id() { + return id; + } + + public boolean isTransitive() { + return transitive; + } + + public boolean is(String id) { + return id().equals(id); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java new file mode 100644 index 000000000000..f01cb2acb99e --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; + +/** + * An {@link Artifact} that has been resolved, i.e. downloaded to the local repository. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface DownloadedArtifact extends Artifact { + + /** + * {@return the a path to the file that has been downloaded to the file system}. + */ + Path getPath(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedDependency.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedDependency.java new file mode 100644 index 000000000000..7a1e4174ed75 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedDependency.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; + +/** + * A {@link Dependency} that has been resolved, i.e. downloaded to the local repository. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface DownloadedDependency extends Dependency, DownloadedArtifact {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Event.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Event.java new file mode 100644 index 000000000000..0323abd43579 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Event.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Event sent by maven during various phases of the build process. + * Such events can be listened to using {@link Listener}s objects + * registered in the {@link Session}. + * + * @since 4.0.0 + */ +@Experimental +public interface Event { + + /** + * Gets the type of the event. + * + * @return the type of the event, never {@code null} + */ + @Nonnull + EventType getType(); + + /** + * Gets the session from which this event originates. + * + * @return the current session, never {@code null} + */ + @Nonnull + Session getSession(); + + /** + * Gets the current project (if any). + * + * @return the current project or {@code empty()} if not applicable + */ + @Nonnull + Optional getProject(); + + /** + * Gets the current mojo execution (if any). + * + * @return the current mojo execution or {@code empty()} if not applicable + */ + @Nonnull + Optional getMojoExecution(); + + /** + * Gets the exception that caused the event (if any). + * + * @return the exception or {@code empty()} if none + */ + Optional getException(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/EventType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/EventType.java new file mode 100644 index 000000000000..63d3479fbf39 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/EventType.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The possible types of execution events. + * + * @since 4.0.0 + */ +@Experimental +public enum EventType { + PROJECT_DISCOVERY_STARTED, + SESSION_STARTED, + SESSION_ENDED, + PROJECT_SKIPPED, + PROJECT_STARTED, + PROJECT_SUCCEEDED, + PROJECT_FAILED, + MOJO_SKIPPED, + MOJO_STARTED, + MOJO_SUCCEEDED, + MOJO_FAILED, + FORK_STARTED, + FORK_SUCCEEDED, + FORK_FAILED, + FORKED_PROJECT_STARTED, + FORKED_PROJECT_SUCCEEDED, + FORKED_PROJECT_FAILED, +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Exclusion.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Exclusion.java new file mode 100644 index 000000000000..99f9263c32d4 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Exclusion.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nullable; + +/** + * A dependency exclusion. + * + * @since 4.0.0 + * @see DependencyCoordinates#getExclusions() + */ +@Experimental +public interface Exclusion { + @Nullable + String getGroupId(); + + @Nullable + String getArtifactId(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnum.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnum.java new file mode 100644 index 000000000000..4b8b74ca0ba9 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnum.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Interface that defines some kind of enums that can be extended by Maven plugins or extensions. + * + * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface + * can be used as keys. + * + * @since 4.0.0 + */ +@Experimental +public interface ExtensibleEnum { + + /** + * The {@code id} uniquely represents a value for this extensible enum. + * This id should be used to compute the equality and hash code for the instance. + * + * @return the id + */ + @Nonnull + String id(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java new file mode 100644 index 000000000000..75892c765140 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Utility class for creating extensible enum implementations. + * This class provides factory methods for creating instances of extensible enums + * such as Language, PathScope, and ProjectScope. + * + * @since 4.0.0 + */ +abstract class ExtensibleEnums { + + /** + * Creates a new Language instance with the specified ID. + * + * @param id the identifier for the language + * @return a new Language instance + */ + static Language language(String id) { + return new DefaultLanguage(id); + } + + /** + * Creates a new PathScope instance with the specified ID, project scope, and dependency scopes. + * + * @param id the identifier for the path scope + * @param projectScope the project scope associated with this path scope + * @param dependencyScopes the dependency scopes associated with this path scope + * @return a new PathScope instance + */ + static PathScope pathScope(String id, ProjectScope projectScope, DependencyScope... dependencyScopes) { + return new DefaultPathScope(id, projectScope, dependencyScopes); + } + + /** + * Creates a new ProjectScope instance with the specified ID. + * + * @param id the identifier for the project scope + * @return a new ProjectScope instance + */ + static ProjectScope projectScope(String id) { + return new DefaultProjectScope(id); + } + + /** + * Base implementation of the ExtensibleEnum interface. + * Provides common functionality for all extensible enum implementations. + */ + private static class DefaultExtensibleEnum implements ExtensibleEnum { + + private final String id; + + /** + * Creates a new DefaultExtensibleEnum with the specified ID. + * + * @param id the identifier for this enum value, must not be null + */ + DefaultExtensibleEnum(String id) { + this.id = Objects.requireNonNull(id); + } + + /** + * Returns the identifier for this enum value. + * + * @return the identifier + */ + @Override + public String id() { + return id; + } + + @Override + public int hashCode() { + return id().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && getClass() == obj.getClass() && id().equals(((DefaultExtensibleEnum) obj).id()); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + id() + "]"; + } + } + + /** + * Implementation of the PathScope interface. + */ + private static class DefaultPathScope extends DefaultExtensibleEnum implements PathScope { + private final ProjectScope projectScope; + private final Set dependencyScopes; + + /** + * Creates a new DefaultPathScope with the specified ID, project scope, and dependency scopes. + * + * @param id the identifier for this path scope + * @param projectScope the project scope associated with this path scope, must not be null + * @param dependencyScopes the dependency scopes associated with this path scope, must not be null + */ + DefaultPathScope(String id, ProjectScope projectScope, DependencyScope... dependencyScopes) { + super(id); + this.projectScope = Objects.requireNonNull(projectScope); + this.dependencyScopes = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(Objects.requireNonNull(dependencyScopes)))); + } + + /** + * Returns the project scope associated with this path scope. + * + * @return the project scope + */ + @Override + public ProjectScope projectScope() { + return projectScope; + } + + /** + * Returns the dependency scopes associated with this path scope. + * + * @return an unmodifiable set of dependency scopes + */ + @Override + public Set dependencyScopes() { + return dependencyScopes; + } + } + + /** + * Implementation of the ProjectScope interface. + */ + private static class DefaultProjectScope extends DefaultExtensibleEnum implements ProjectScope { + + /** + * Creates a new DefaultProjectScope with the specified ID. + * + * @param id the identifier for this project scope + */ + DefaultProjectScope(String id) { + super(id); + } + } + + /** + * Implementation of the Language interface. + */ + private static class DefaultLanguage extends DefaultExtensibleEnum implements Language { + + /** + * Creates a new DefaultLanguage with the specified ID. + * + * @param id the identifier for this language + */ + DefaultLanguage(String id) { + super(id); + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java new file mode 100644 index 000000000000..63188ec6fd37 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java @@ -0,0 +1,392 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import javax.tools.DocumentationTool; +import javax.tools.JavaFileManager; +import javax.tools.StandardLocation; + +import java.io.File; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * The option of a Java command-line tool where to place the paths to some dependencies. + * A {@code PathType} can identify the class path, the module path, the patches for a specific module, + * or another kind of path. + * + *

One path type is handled in a special way: unlike other options, + * the paths specified in a {@code --patch-module} Java option is effective only for a specified module. + * This type is created by calls to {@link #patchModule(String)} and a new instance must be created for + * every module to patch.

+ * + *

Path types are often exclusive. For example, a dependency should not be both on the Java class path + * and on the Java module path.

+ * + *

Relationship with Java compiler standard location

+ * This enumeration is closely related to the {@link JavaFileManager.Location} enumerations. + * A difference is that the latter enumerates input and output files, while {@code JavaPathType} + * enumerates only input dependencies. Another difference is that {@code JavaPathType} contains + * some enumeration values used only at runtime and therefore not available in {@code javax.tool}, + * such as agent paths. + * + * @see org.apache.maven.api.services.DependencyResolverResult#getDispatchedPaths() + * + * @since 4.0.0 + */ +@Experimental +public enum JavaPathType implements PathType { + /** + * The path identified by the Java {@code --class-path} option. + * Used for compilation, execution and Javadoc among others. + * The Java tools location is {@link StandardLocation#CLASS_PATH}. + * + *

Context-sensitive interpretation

+ * A dependency with this path type will not necessarily be placed on the class path. + * There are two circumstances where the dependency may nevertheless be placed somewhere else: + * + *
    + *
  • If {@link #MODULES} path type is also set, then the dependency can be placed either on the + * class path or on the module path, but only one of those. The choice is up to the plugin, + * possibly using heuristic rules (Maven 3 behavior).
  • + *
  • If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module path, + * then the test dependency will be placed on the Java {@code --patch-module} option instead of the + * class path.
  • + *
+ */ + CLASSES(StandardLocation.CLASS_PATH, "--class-path"), + + /** + * The path identified by the Java {@code --module-path} option. + * Used for compilation, execution and Javadoc among others. + * The Java tools location is {@link StandardLocation#MODULE_PATH}. + * + *

Context-sensitive interpretation

+ * A dependency with this flag will not necessarily be placed on the module path. + * There are two circumstances where the dependency may nevertheless be placed somewhere else: + * + *
    + *
  • If {@link #CLASSES} path type is also set, then the dependency should be placed on the + * module path, but is also compatible with placement on the class path. Compatibility can + * be achieved, for example, by repeating in the {@code META-INF/services/} directory the services + * that are declared in the {@code module-info.class} file. In that case, the path type can be chosen + * by the plugin.
  • + *
  • If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module path, + * then the test dependency will be placed on the Java {@code --patch-module} option instead of the + * {@code --module-path} option.
  • + *
+ */ + MODULES(StandardLocation.MODULE_PATH, "--module-path"), + + /** + * The path identified by the Java {@code --upgrade-module-path} option. + * The Java tools location is {@link StandardLocation#UPGRADE_MODULE_PATH}. + */ + UPGRADE_MODULES(StandardLocation.UPGRADE_MODULE_PATH, "--upgrade-module-path"), + + /** + * The path identified by the Java {@code --patch-module} option. + * The Java tools location is {@link StandardLocation#PATCH_MODULE_PATH}. + * + * Note that this option is incomplete, because it must be followed by a module name. + * Use this type only when the module to patch is unknown. + * + * @see #patchModule(String) + */ + PATCH_MODULE(StandardLocation.PATCH_MODULE_PATH, "--patch-module"), + + /** + * The path identified by the Java {@code --processor-path} option. + * The Java tools location is {@link StandardLocation#ANNOTATION_PROCESSOR_PATH}. + */ + PROCESSOR_CLASSES(StandardLocation.ANNOTATION_PROCESSOR_PATH, "--processor-path"), + + /** + * The path identified by the Java {@code --processor-module-path} option. + * The Java tools location is {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH}. + */ + PROCESSOR_MODULES(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, "--processor-module-path"), + + /** + * The path identified by the Java {@code -agentpath} option. + */ + AGENT(null, "-agentpath"), + + /** + * The path identified by the Javadoc {@code -doclet} option. + * The Java tools location is {@link DocumentationTool.Location#DOCLET_PATH}. + */ + DOCLET(DocumentationTool.Location.DOCLET_PATH, "-doclet"), + + /** + * The path identified by the Javadoc {@code -tagletpath} option. + * The Java tools location is {@link DocumentationTool.Location#TAGLET_PATH}. + */ + TAGLETS(DocumentationTool.Location.TAGLET_PATH, "-tagletpath"); + + /** + * Creates a path identified by the Java {@code --patch-module} option. + * Contrarily to the other types of paths, this path is applied to only + * one specific module. Used for compilation and execution among others. + * + *

Context-sensitive interpretation

+ * This path type makes sense only when a main module is added on the module path by another dependency. + * In no main module is found, the patch dependency may be added on the class path or module path + * depending on whether {@link #CLASSES} or {@link #MODULES} is present. + * + * @param moduleName name of the module on which to apply the path + * @return an identification of the patch-module path for the given module. + * + * @see Modular#moduleName() + */ + @Nonnull + public static Modular patchModule(@Nonnull String moduleName) { + return PATCH_MODULE.new Modular(moduleName); + } + + /** + * The {@code javax.tool} enumeration value corresponding to this {@code JavaPathType}, or {@code null} if none. + * + * @see #location() + */ + private final JavaFileManager.Location location; + + /** + * The tools option for this path, or {@code null} if none. + * + * @see #option() + */ + private final String option; + + /** + * Creates a new enumeration value for a path associated to the given tool option. + * + * @param location the {@code javax.tool} enumeration value, or {@code null} if none. + * @param option the Java tools option for this path, or {@code null} if none + */ + JavaPathType(JavaFileManager.Location location, String option) { + this.location = location; + this.option = option; + } + + /** + * Returns the unique name of this path type. + * + * @return the programmatic name of this enumeration value + */ + @Override + public String id() { + return name(); + } + + /** + * Returns the identification of this path in the {@code javax.tool} API. + * The value may be an instance of {@link StandardLocation} or {@link DocumentationTool.Location}, + * depending on which tool will use this location. + * + * @return the {@code javax.tool} enumeration value corresponding to this {@code JavaPathType} + */ + public Optional location() { + return Optional.ofNullable(location); + } + + /** + * Returns the path type associated to the given {@code javax.tool} location. + * This method is the converse of {@link #location()}. + * + * @param location identification of a path in the {@code javax.tool} API + * @return Java path type associated to the given location + */ + public static Optional valueOf(JavaFileManager.Location location) { + for (JavaPathType type : JavaPathType.values()) { + if (location.equals(type.location)) { + return Optional.of(type); + } + } + return Optional.empty(); + } + + /** + * Returns the name of the tool option for this path. For example, if this path type + * is {@link #MODULES}, then this method returns {@code "--module-path"}. The option + * does not include the {@linkplain Modular#moduleName() module name} on which it applies. + * + * @return the name of the tool option for this path type + */ + @Nonnull + @Override + public Optional option() { + return Optional.ofNullable(option); + } + + /** + * Returns the option followed by a string representation of the given path elements. + * For example, if this type is {@link #MODULES}, then the option is {@code "--module-path"} + * followed by the specified path elements. + * + * @param paths the path to format as a tool option + * @return the option associated to this path type followed by the given path elements, + * or an empty array if there is no path element + * @throws IllegalStateException if no option is associated to this path type + */ + @Nonnull + @Override + public String[] option(Iterable paths) { + return format(null, paths); + } + + /** + * Implementation shared with {@link Modular}. + */ + final String[] format(String moduleName, Iterable paths) { + if (option == null) { + throw new IllegalStateException("No option is associated to this path type."); + } + String prefix = (moduleName == null) ? "\"" : (moduleName + "=\""); + StringJoiner joiner = new StringJoiner(File.pathSeparator, prefix, "\""); + joiner.setEmptyValue(""); + for (Path p : paths) { + joiner.add(p.toString()); + } + String value = joiner.toString(); + if (value.isEmpty()) { + return new String[0]; + } + return new String[] {option, value}; + } + + /** + * {@return a string representation of this path type for debugging purposes}. + */ + @Override + public String toString() { + return "PathType[" + id() + "]"; + } + + /** + * Type of path which is applied to only one specific Java module. + * The main case is the Java {@code --patch-module} option. + * + * @see #PATCH_MODULE + * @see #patchModule(String) + */ + public final class Modular implements PathType { + /** + * Name of the module for which a path is specified. + */ + @Nonnull + private final String moduleName; + + /** + * Creates a new path type for the specified module. + * + * @param moduleName name of the module for which a path is specified + */ + private Modular(@Nonnull String moduleName) { + this.moduleName = Objects.requireNonNull(moduleName); + } + + /** + * Returns the type of path without indication about the target module. + * This is usually {@link #PATCH_MODULE}. + * + * @return type of path without indication about the target module + */ + @Nonnull + public JavaPathType rawType() { + return JavaPathType.this; + } + + /** + * Returns the name of the tool option for this path, including the module name. + * + * @return name of the tool option for this path, including the module name + */ + @Override + public String id() { + return JavaPathType.this.name() + ":" + moduleName; + } + + /** + * Returns the name of the tool option for this path, not including the module name. + * + * @return name of the tool option for this path, not including the module name + */ + @Nonnull + @Override + public String name() { + return JavaPathType.this.name(); + } + + /** + * Returns the name of the module for which a path is specified + * + * @return name of the module for which a path is specified + */ + @Nonnull + public String moduleName() { + return moduleName; + } + + /** + * Returns the name of the tool option for this path. + * The option does not include the {@linkplain #moduleName() module name} on which it applies. + * + * @return the name of the tool option for this path type + */ + @Nonnull + @Override + public Optional option() { + return JavaPathType.this.option(); + } + + /** + * Returns the option followed by a string representation of the given path elements. + * The path elements are separated by an option-specific or platform-specific separator. + * If the given {@code paths} argument contains no element, then this method returns an empty string. + * + * @param paths the path to format as a string + * @return the option associated to this path type followed by the given path elements, + * or an empty array if there is no path element. + */ + @Nonnull + @Override + public String[] option(Iterable paths) { + return format(moduleName, paths); + } + + /** + * Returns the programmatic name of this path type, including the module to patch. + * For example, if this type was created by {@code JavaPathType.patchModule("foo.bar")}, + * then this method returns {@code "PathType[PATCH_MODULE:foo.bar]")}. + * + * @return the programmatic name together with the module name on which it applies + */ + @Nonnull + @Override + public String toString() { + return "PathType[" + id() + "]"; + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/JavaToolchain.java b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaToolchain.java new file mode 100644 index 000000000000..a4addddac79a --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaToolchain.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Represents a Java toolchain in the Maven build system. + * + *

A Java toolchain is a specific type of toolchain that provides access + * to Java development tools, such as the Java compiler and Java runtime + * environment. This interface allows users to define and configure + * Java-related toolchains that can be utilized during the build process + * in Maven.

+ * + *

Java toolchains are defined in the Maven toolchains.xml file and can + * be referenced in the project's POM file. This enables developers to + * specify the exact versions of Java tools they wish to use, ensuring + * consistency across different build environments.

+ * + * @since 4.0.0 + * @see Toolchain + * @see org.apache.maven.api.services.ToolchainManager + */ +@Experimental +public interface JavaToolchain extends Toolchain { + + String getJavaHome(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Language.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Language.java new file mode 100644 index 000000000000..39a5c46e6ae6 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Language.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; + +import static org.apache.maven.api.ExtensibleEnums.language; + +/** + * Language. + *

+ * This extensible enum has two defined values, {@link #NONE} and {@link #JAVA_FAMILY}, + * but can be extended by registering a {@code org.apache.maven.api.spi.LanguageProvider}. + *

+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface + * can be used as keys. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +@SuppressWarnings("checkstyle:InterfaceIsType") +public interface Language extends ExtensibleEnum { + + /** + * The "none" language. It is not versioned, family is same to itself, and compatible with itself only. + * In turn, every {@link Language} implementation must be compatible with {@code NONE} language. + */ + Language NONE = language("none"); + + /** + * The "resources" language. This is used for files such as images to provide in the output. + */ + Language RESOURCES = language("resources"); + + /** + * The "script" language. Provided for compatibility with Maven 3. + * + * @deprecated Use {@link #RESOURCES} instead. + */ + @Deprecated + Language SCRIPT = language("script"); + + // TODO: this should be moved out from here to Java Support (builtin into core) + Language JAVA_FAMILY = language("java"); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Lifecycle.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Lifecycle.java new file mode 100644 index 000000000000..27cb57d7c480 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Lifecycle.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Plugin; + +/** + * A Maven lifecycle is a sequence of predefined phases that govern the build process + * of a Maven project. Each phase represents a specific step, such as compiling the + * code, running tests, packaging the project, and deploying it. Executing a phase + * triggers all preceding phases, ensuring that each step of the build process is + * completed in the correct order. The three main lifecycles in Maven are + * {@link #DEFAULT default}, {@link #CLEAN clean}, and {@link #SITE site}, with the + * {@code default} lifecycle being the most commonly used for project builds. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Lifecycle extends ExtensibleEnum { + + // ========================= + // Maven defined lifecycles + // ========================= + String CLEAN = "clean"; + String DEFAULT = "default"; + String SITE = "site"; + + // ====================== + // Phase qualifiers + // ====================== + String BEFORE = "before:"; + String AFTER = "after:"; + String AT = "at:"; + + /** + * Name or identifier of this lifecycle. + * + * @return the unique identifier for this lifecycle + */ + @Override + String id(); + + /** + * Collection of main phases for this lifecycle. + * + * @return the collection of top-level phases in this lifecycle + */ + Collection phases(); + + /** + * Collection of main phases for this lifecycle used with the Maven 3 builders. + * Those builders do not operate on a graph, but on the list and expect a slightly + * different ordering (mainly unit test being executed before packaging). + * + * @return the collection of phases in Maven 3 compatible ordering + */ + default Collection v3phases() { + return phases(); + } + + /** + * Stream of phases containing all child phases recursively. + * + * @return a stream of all phases in this lifecycle, including nested phases + */ + default Stream allPhases() { + return phases().stream().flatMap(Phase::allPhases); + } + + /** + * Collection of aliases for this lifecycle. + * Aliases map Maven 3 phase names to their Maven 4 equivalents. + * + * @return the collection of phase aliases + */ + Collection aliases(); + + /** + * A phase in the lifecycle. + * + * A phase is identified by its name. It also contains a list of plugins bound to that phase, + * a list of {@link Link links}, and a list of sub-phases. This forms a tree of phases. + */ + interface Phase { + + // ====================== + // Maven defined phases + // ====================== + String ALL = "all"; + String EACH = "each"; + String BUILD = "build"; + String INITIALIZE = "initialize"; + String VALIDATE = "validate"; + String SOURCES = "sources"; + String RESOURCES = "resources"; + String COMPILE = "compile"; + String READY = "ready"; + String PACKAGE = "package"; + String VERIFY = "verify"; + String UNIT_TEST = "unit-test"; + String TEST_SOURCES = "test-sources"; + String TEST_RESOURCES = "test-resources"; + String TEST_COMPILE = "test-compile"; + String TEST = "test"; + String INTEGRATION_TEST = "integration-test"; + String INSTALL = "install"; + String DEPLOY = "deploy"; + String CLEAN = "clean"; + + /** + * Returns the name of this phase. + * + * @return the phase name + */ + @Nonnull + String name(); + + /** + * Returns the list of plugins bound to this phase. + * + * @return the list of plugins + */ + @Nonnull + List plugins(); + + /** + * Returns the collection of links from this phase to other phases. + * + * @return the collection of links + */ + @Nonnull + Collection links(); + + /** + * {@return the list of sub-phases} + */ + @Nonnull + List phases(); + + /** + * Returns a stream of all phases, including this phase and all nested phases. + * + * @return a stream of all phases + */ + @Nonnull + Stream allPhases(); + } + + /** + * A phase alias, mostly used to support the Maven 3 phases which are mapped + * to dynamic phases in Maven 4. + */ + interface Alias { + /** + * Returns the Maven 3 phase name. + * + * @return the Maven 3 phase name + */ + String v3Phase(); + + /** + * Returns the Maven 4 phase name. + * + * @return the Maven 4 phase name + */ + String v4Phase(); + } + + /** + * A link from a phase to another phase, consisting of a type which can be + * {@link Kind#BEFORE} or {@link Kind#AFTER}, and a {@link Pointer} to + * another phase. + */ + interface Link { + enum Kind { + BEFORE, + AFTER + } + + /** + * Returns the kind of link (BEFORE or AFTER). + * + * @return the link kind + */ + Kind kind(); + + /** + * Returns the pointer to the target phase. + * + * @return the phase pointer + */ + Pointer pointer(); + } + + interface Pointer { + enum Type { + PROJECT, + DEPENDENCIES, + CHILDREN + } + + /** + * Returns the name of the target phase. + * + * @return the phase name + */ + String phase(); + + /** + * Returns the type of pointer (PROJECT, DEPENDENCIES, or CHILDREN). + * + * @return the pointer type + */ + Type type(); + } + + interface PhasePointer extends Pointer { + /** + * Returns the type of pointer, which is always PROJECT for a PhasePointer. + * + * @return the PROJECT pointer type + */ + @Override + default Type type() { + return Type.PROJECT; + } + } + + interface DependenciesPointer extends Pointer { + /** + * Returns the dependency scope this pointer applies to. + * + * @return the dependency scope, or "all" if not specified + */ + String scope(); // default: all + + /** + * Returns the type of pointer, which is always DEPENDENCIES for a DependenciesPointer. + * + * @return the DEPENDENCIES pointer type + */ + @Override + default Type type() { + return Type.DEPENDENCIES; + } + } + + interface ChildrenPointer extends Pointer { + /** + * Returns the type of pointer, which is always CHILDREN for a ChildrenPointer. + * + * @return the CHILDREN pointer type + */ + @Override + default Type type() { + return Type.CHILDREN; + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Listener.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Listener.java new file mode 100644 index 000000000000..dda744f7375a --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Listener.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * A listener for session events. + * TODO: open this to other events like similar to {@code org.apache.maven.eventspy.EventSpy} + * + * @since 4.0.0 + */ +@Experimental +@FunctionalInterface +@Consumer +public interface Listener { + void onEvent(@Nonnull Event event); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/LocalRepository.java b/api/maven-api-core/src/main/java/org/apache/maven/api/LocalRepository.java new file mode 100644 index 000000000000..cea61bc3c504 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/LocalRepository.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + *

The local repository is a directory on the developer's machine where + * Maven stores all the downloaded artifacts (such as dependencies, plugins, + * and project artifacts). When Maven builds a project, it first checks the + * local repository to see if the required artifacts are already available. + * If the artifacts are found locally, Maven uses them directly, which speeds + * up the build process by avoiding unnecessary downloads.

+ * + *

By default, the local repository is located in the {@code .m2/repository} + * directory within the user's home directory ({@code ~/.m2/repository} on + * Unix-like systems or {@code C:\Users\YourName\.m2\repository} on Windows). + * The location of the local repository can be customized in the + * {@code settings.xml} file.

+ * + * @since 4.0.0 + * @see Repository + * @see org.apache.maven.api.settings.Settings + */ +@Experimental +@Immutable +public interface LocalRepository extends Repository { + + @Nonnull + Path getPath(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/MojoExecution.java b/api/maven-api-core/src/main/java/org/apache/maven/api/MojoExecution.java new file mode 100644 index 000000000000..15ffac662fb9 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/MojoExecution.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.PluginExecution; +import org.apache.maven.api.plugin.descriptor.MojoDescriptor; +import org.apache.maven.api.xml.XmlNode; + +/** + * A {@code MojoExecution} represents a single execution of a Maven Plugin during a given build. + * An instance of this object is bound to the {@link org.apache.maven.api.di.MojoExecutionScoped} + * and available as {@code mojoExecution} within {@link org.apache.maven.api.plugin.annotations.Parameter} + * expressions. + * + * @since 4.0.0 + */ +@Experimental +public interface MojoExecution { + + @Nonnull + Plugin getPlugin(); + + @Nonnull + PluginExecution getModel(); + + @Nonnull + MojoDescriptor getDescriptor(); + + @Nonnull + String getExecutionId(); + + @Nonnull + String getGoal(); + + @Nonnull + String getLifecyclePhase(); + + @Nonnull + Optional getConfiguration(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/MonotonicClock.java b/api/maven-api-core/src/main/java/org/apache/maven/api/MonotonicClock.java new file mode 100644 index 000000000000..963618e59fcb --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/MonotonicClock.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; + +/** + * A Clock implementation that combines monotonic timing with wall-clock time. + *

+ * This class provides precise time measurements using {@link System#nanoTime()} + * while maintaining wall-clock time information in UTC. The wall-clock time + * is computed from the monotonic duration since system start to ensure consistency + * between time measurements. + *

+ * This implementation is singleton-based and always uses UTC timezone. The clock + * cannot be adjusted to different timezones to maintain consistent monotonic behavior. + * Users needing local time representation should convert the result of {@link #instant()} + * to their desired timezone: + *

{@code
+ * Instant now = MonotonicClock.now();
+ * ZonedDateTime local = now.atZone(ZoneId.systemDefault());
+ * }
+ * + * @see System#nanoTime() + * @see Clock + */ +public class MonotonicClock extends Clock { + private static final MonotonicClock CLOCK = new MonotonicClock(); + + private final long startNanos; + private final Instant startInstant; + + /** + * Private constructor to enforce singleton pattern. + * Initializes the clock with the current system time and nanoTime. + */ + private MonotonicClock() { + this.startNanos = System.nanoTime(); + this.startInstant = Clock.systemUTC().instant(); + } + + /** + * Returns the singleton instance of MonotonicClock. + * + * @return the monotonic clock instance + */ + public static MonotonicClock get() { + return CLOCK; + } + + /** + * Returns the current instant from the monotonic clock. + * This is a convenience method equivalent to {@code get().instant()}. + * + * @return the current instant using monotonic timing + */ + public static Instant now() { + return get().instant(); + } + + /** + * Returns the initialization time of this monotonic clock. + * This is a convenience method equivalent to {@code get().start()}. + * + * @return the instant when this monotonic clock was initialized + * @see #startInstant() + */ + public static Instant start() { + return get().startInstant(); + } + + /** + * Returns the elapsed time since clock initialization. + * This is a convenience method equivalent to {@code get().elapsedTime()}. + * + * @return the duration since clock initialization + */ + public static Duration elapsed() { + return get().elapsedTime(); + } + + /** + * Returns a monotonically increasing instant. + *

+ * The returned instant is calculated by adding the elapsed nanoseconds + * since clock creation to the initial wall clock time. This ensures that + * the time never goes backwards and maintains a consistent relationship + * with the wall clock time. + * + * @return the current instant using monotonic timing + */ + @Override + public Instant instant() { + long elapsedNanos = System.nanoTime() - startNanos; + return startInstant.plusNanos(elapsedNanos); + } + + /** + * Returns the wall clock time captured when this monotonic clock was initialized. + *

+ * This instant serves as the base time from which all subsequent {@link #instant()} + * calls are calculated by adding the elapsed monotonic duration. This ensures + * consistency between the monotonic measurements and wall clock time. + * + * @return the initial wall clock instant when this clock was created + * @see #instant() + */ + public Instant startInstant() { + return startInstant; + } + + /** + * Returns the duration elapsed since this clock was initialized. + *

+ * The returned duration is calculated using {@link System#nanoTime()} + * to ensure monotonic behavior. This duration represents the exact time + * span between clock initialization and the current instant. + * + * @return the duration since clock initialization + * @see #startInstant() + * @see #instant() + */ + public Duration elapsedTime() { + long elapsedNanos = System.nanoTime() - startNanos; + return Duration.ofNanos(elapsedNanos); + } + + /** + * Returns the zone ID of this clock, which is always UTC. + * + * @return the UTC zone ID + */ + @Override + public ZoneId getZone() { + return ZoneOffset.UTC; + } + + /** + * Returns this clock since timezone adjustments are not supported. + *

+ * This implementation maintains UTC time to ensure monotonic behavior. + * The provided zone parameter is ignored. + * + * @param zone the target timezone (ignored) + * @return this clock instance + */ + @Override + public Clock withZone(ZoneId zone) { + // Monotonic clock is always UTC-based + return this; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Node.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Node.java new file mode 100644 index 000000000000..ba0e5f108efa --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Node.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.annotations.Provider; + +/** + * Represents a dependency node within a Maven project's dependency collector. + * + * @since 4.0.0 + * @see org.apache.maven.api.services.DependencyResolverResult#getRoot() + */ +@Experimental +@Immutable +@Provider +public interface Node { + + /** + * @return artifact for this node + */ + @Nullable + Artifact getArtifact(); + + /** + * @return dependency for this node + */ + @Nullable + Dependency getDependency(); + + /** + * Gets the child nodes of this node. + * + * @return the child nodes of this node, never {@code null} + */ + @Nonnull + List getChildren(); + + /** + * @return repositories of this node + */ + @Nonnull + List getRemoteRepositories(); + + /** + * The repository where this artifact has been downloaded from. + */ + @Nonnull + Optional getRepository(); + + /** + * Traverses this node and potentially its children using the specified visitor. + * + * @param visitor the visitor to call back, must not be {@code null} + * @return {@code true} to visit siblings nodes of this node as well, {@code false} to skip siblings + */ + boolean accept(@Nonnull NodeVisitor visitor); + + /** + * Returns a new tree starting at this node, filtering the children. + * Note that this node will not be filtered and only the children + * and its descendant will be checked. + * + * @param filter the filter to apply + * @return a new filtered graph + */ + @Nonnull + Node filter(@Nonnull Predicate filter); + + /** + * Returns a detailed string representation of this dependency node. + *

+ * When verbose mode is disabled, returns the basic string representation in the format: + * {@code groupId:artifactId:version[:scope]} + *

+ * When verbose mode is enabled, additional details are included with the following format: + *

    + *
  • For included dependencies: {@code groupId:artifactId:version[:scope] (details)}
  • + *
  • For omitted dependencies: {@code (groupId:artifactId:version[:scope] - details)}
  • + *
+ * Where details may include: + *
    + *
  • Version management information (if the version was managed from a different version)
  • + *
  • Scope management information (if the scope was managed from a different scope)
  • + *
  • Scope updates (if the scope was changed during resolution)
  • + *
  • Conflict resolution information (if the dependency was omitted due to conflicts or duplicates)
  • + *
+ * + * @return a string representation of this dependency node with optional detailed information + */ + @Nonnull + String asString(); + + /** + * Obtain a Stream containing this node and all its descendants. + * + * @return a stream containing this node and its descendants + */ + @Nonnull + default Stream stream() { + return Stream.concat(Stream.of(this), getChildren().stream().flatMap(Node::stream)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/NodeVisitor.java b/api/maven-api-core/src/main/java/org/apache/maven/api/NodeVisitor.java new file mode 100644 index 000000000000..a09fc95f5ee5 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/NodeVisitor.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Defines a hierarchical visitor for collecting dependency node trees. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface NodeVisitor { + /** + * Starts the visit to the specified dependency node. + * + * @param node the dependency node to visit + * @return true to visit the specified dependency node's children, false to skip the + * specified dependency node's children and proceed to its next sibling + */ + boolean enter(@Nonnull Node node); + + /** + * Ends the visit to the specified dependency node. + * + * @param node the dependency node to visit + * @return true to visit the specified dependency node's next sibling, false to skip the + * specified dependency node's next siblings and proceed to its parent + */ + boolean leave(@Nonnull Node node); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java new file mode 100644 index 000000000000..18437f9c7773 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Map; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.PluginContainer; + +/** + * Represents the packaging of a Maven project. + * + *

The {@code Packaging} class defines the type of artifact that a Maven project produces during the build process. + * The packaging type determines the structure of the project's output and how Maven will treat the resulting artifact.

+ * + *

Common packaging types include {@code jar}, {@code war}, {@code pom}, {@code maven-plugin}, {@code ear}, and others. + * These types influence various aspects of the build lifecycle, such as which plugins are executed and how dependencies are managed.

+ * + *

The {@code Packaging} class is an immutable value object, ensuring that once a packaging type is defined, it cannot be changed.

+ * + *

Standard Packaging Types

+ *
    + *
  • {@code jar}: Packages the project as a Java Archive (JAR) file.
  • + *
  • {@code war}: Packages the project as a Web Application Archive (WAR) file.
  • + *
  • {@code pom}: Indicates that the project does not produce a deployable artifact but is used for dependency management or as an aggregator.
  • + *
  • {@code maven-plugin}: Packages the project as a Maven plugin.
  • + *
+ * + *

Usage Example

+ * + *
+ * {@code
+ * Session session = ... // Obtain a Maven session
+ * Packaging packaging = session.requirePackaging("jar");
+ * System.out.println(packaging.getId()); // Outputs "jar"
+ * }
+ * 
+ * + * @see org.apache.maven.api.Session#requirePackaging(String) + * @see org.apache.maven.api.Project#getPackaging() + * @see org.apache.maven.api.model.Model#getPackaging() + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Packaging extends ExtensibleEnum { + /** + * The packaging id. + */ + @Nonnull + @Override + String id(); + + /** + * The language of this packaging. + */ + @Nonnull + default Language language() { + return type().getLanguage(); + } + + /** + * The type of main artifact produced by this packaging. + */ + @Nonnull + Type type(); + + /** + * Returns the binding to use specifically for this packaging keyed by lifecycle id. + * This will be used instead of the default packaging definition. + */ + @Nonnull + Map plugins(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java new file mode 100644 index 000000000000..bd8a6f1bf51c --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Set; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +import static org.apache.maven.api.ExtensibleEnums.pathScope; + +/** + * Path scope. + * A path scope is used to determine the kind of build or class path that will be built when resolving + * dependencies using the {@link org.apache.maven.api.services.DependencyResolver} service. + *

+ * This extensible enum has four defined values, {@link #MAIN_COMPILE}, {@link #MAIN_RUNTIME}, + * {@link #TEST_COMPILE} and {@link #TEST_RUNTIME}, but can be extended by registering a + * {@code org.apache.maven.api.spi.PathScopeProvider}. + *

+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface + * can be used as keys. + * + * @since 4.0.0 + * @see org.apache.maven.api.services.DependencyResolver + * @see DependencyScope + */ +@Experimental +@Immutable +public interface PathScope extends ExtensibleEnum { + + // TODO: what if I simply want all dependencies ? + @Nonnull + ProjectScope projectScope(); + + @Nonnull + Set dependencyScopes(); + + PathScope MAIN_COMPILE = pathScope( + "main-compile", + ProjectScope.MAIN, + DependencyScope.COMPILE_ONLY, + DependencyScope.COMPILE, + DependencyScope.PROVIDED); + + PathScope MAIN_RUNTIME = + pathScope("main-runtime", ProjectScope.MAIN, DependencyScope.COMPILE, DependencyScope.RUNTIME); + + PathScope TEST_COMPILE = pathScope( + "test-compile", + ProjectScope.TEST, + DependencyScope.COMPILE, + DependencyScope.PROVIDED, + DependencyScope.TEST_ONLY, + DependencyScope.TEST); + + PathScope TEST_RUNTIME = pathScope( + "test-runtime", + ProjectScope.TEST, + DependencyScope.COMPILE, + DependencyScope.RUNTIME, + DependencyScope.PROVIDED, + DependencyScope.TEST, + DependencyScope.TEST_RUNTIME); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java new file mode 100644 index 000000000000..5c2e302dca83 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * The option of a command-line tool where to place the paths to some dependencies. + * A {@code PathType} can identify the Java class-path, the Java module path, + * or another kind of path for another programming language for example. + * Path types are often exclusive. For example, a dependency should not be + * both on the Java class path and on the Java module path. + * + * @see org.apache.maven.api.services.DependencyResolverResult#getDispatchedPaths() + * + * @since 4.0.0 + */ +@Experimental +public interface PathType { + /** + * The type for all paths that could not be placed in any of the types requested by a caller. + * This type can appear in the return value of a call to + * {@link Session#resolveDependencies resolveDependencies(...)} when at least one dependency + * cannot be associated to any type specified in the {@code desiredTypes} argument. + * Plugins can choose to report a warning to users when unresolved paths exist. + */ + PathType UNRESOLVED = new PathType() { + @Override + public String name() { + return "UNRESOLVED"; + } + + @Override + public String id() { + return "UNRESOLVED"; + } + + @Override + public Optional option() { + return Optional.empty(); + } + + @Override + public String[] option(Iterable paths) { + return new String[0]; + } + }; + + /** + * Returns the unique name of this path type, including the module to patch if any. + * For example, if this type is {@link JavaPathType#MODULES}, then this method returns {@code "MODULES"}. + * But if this type was created by {@code JavaPathType.patchModule("foo.bar")}, then this method returns + * {@code "PATCH_MODULE:foo.bar"}. + * + * @return the programmatic name together with the module name on which it applies + * @see #toString() + */ + @Nonnull + String id(); + + /** + * Returns the name of the tool option for this path. For example, if this path type + * is {@link JavaPathType#MODULES}, then this method returns {@code "--module-path"}. + * The option does not include the {@linkplain JavaPathType.Modular#moduleName() module name} + * on which it applies. + * + * @return the name of the tool option for this path type + */ + @Nonnull + Optional option(); + + /** + * Returns the option followed by a string representation of the given path elements. + * The path elements are separated by an option-specific or platform-specific separator. + * If the given {@code paths} argument contains no element, then this method returns an empty string. + * + *

Examples

+ * If {@code paths} is a list containing two elements, {@code dir/path1} and {@code dir/path2}, then: + * + *
    + *
  • If this type is {@link JavaPathType#MODULES}, then this method returns + * {@code {"--module-path", "dir/path1:dir/path2"}} on Unix or + * {@code {"--module-path", "dir\path1;dir\path2"}} on Windows.
  • + *
  • If this type was created by {@code JavaPathType.patchModule("foo.bar")}, then the method returns + * {@code {"--patch-module", "foo.bar=dir/path1:dir/path2"}} on Unix or + * {@code {"--patch-module", "foo.bar=dir\path1;dir\path2"}} on Windows.
  • + *
+ * + * @param paths the path to format as a string + * @return the option associated to this path type followed by the given path elements, + * or an empty array if there is no path element. + */ + @Nonnull + String[] option(Iterable paths); + + /** + * Returns the name of this path type. For example, if this path type + * is {@link JavaPathType#MODULES}, then this method returns {@code "MODULES"}. + * + * @return the programmatic name of this path type + */ + @Nonnull + String name(); + + /** + * {@return a string representation for this extensible enum describing a path type} + * For example {@code "PathType[PATCH_MODULE:foo.bar]"}. + */ + @Nonnull + @Override + String toString(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Plugin.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Plugin.java new file mode 100644 index 000000000000..df26c2b8e9d0 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Plugin.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.plugin.descriptor.PluginDescriptor; +import org.apache.maven.api.plugin.descriptor.lifecycle.Lifecycle; + +/** + * Represents a maven plugin runtime + * + * @since 4.0.0 + */ +@Experimental +public interface Plugin { + + @Nonnull + org.apache.maven.api.model.Plugin getModel(); + + @Nonnull + PluginDescriptor getDescriptor(); + + @Nonnull + List getLifecycles(); + + @Nonnull + ClassLoader getClassLoader(); + + @Nonnull + Artifact getArtifact(); + + @Nonnull + default Collection getDependencies() { + return getDependenciesMap().values(); + } + + @Nonnull + Map getDependenciesMap(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ProducedArtifact.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ProducedArtifact.java new file mode 100644 index 000000000000..6c9e1be33c45 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ProducedArtifact.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; + +/** + * An {@link Artifact} that is being produced by a {@link Project} during the build. + * + *

Produced artifacts includes:

    + *
  • {@linkplain Project#getPomArtifact() the project POM artifact}
  • + *
  • {@linkplain Project#getMainArtifact() the main artifact}
  • + *
  • {@linkplain org.apache.maven.api.services.ProjectManager#attachArtifact(Session, Project, Path) artifacts to be attached to a project}
  • + *
+ * + *

For the main artifact and attached artifacts, the + * {@link org.apache.maven.api.services.ArtifactManager ArtifactManager} service must be used + * to point the artifact to a {@link Path} during the packaging phase.

+ * + * @since 4.0.0 + * @see Project#getMainArtifact() + * @see Project#getPomArtifact() + * @see org.apache.maven.api.services.ProjectManager#attachArtifact(Session, Project, Path) + * @see org.apache.maven.api.services.ArtifactManager#setPath(ProducedArtifact, Path) + */ +@Experimental +@Immutable +public interface ProducedArtifact extends Artifact {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java new file mode 100644 index 000000000000..8e989ad4ae98 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java @@ -0,0 +1,306 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Build; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.model.Profile; + +/** + * Interface representing a Maven project which can be created using the + * {@link org.apache.maven.api.services.ProjectBuilder ProjectBuilder} service. + * Such objects are immutable and plugin that wish to modify such objects + * need to do so using the {@link org.apache.maven.api.services.ProjectManager + * ProjectManager} service. + *

+ * Projects are created using the {@code ProjectBuilder} from a POM file + * (usually named {@code pom.xml}) on the file system. + * The {@link #getPomPath()} will point to the POM file and the + * {@link #getBasedir()} to the directory parent containing the + * POM file. + *

+ * + * @since 4.0.0 + * @see org.apache.maven.api.services.ProjectManager + * @see org.apache.maven.api.services.ProjectBuilder + */ +@Experimental +public interface Project { + + /** + * {@return the project groupId}. + */ + @Nonnull + String getGroupId(); + + /** + * {@return the project artifactId}. + */ + @Nonnull + String getArtifactId(); + + /** + * {@return the project version}. + */ + @Nonnull + String getVersion(); + + /** + * {@return the project packaging}. + *

+ * Note: unlike in legacy code, logical checks against string representing packaging (returned by this method) + * are NOT recommended (code like {@code "pom".equals(project.getPackaging)} must be avoided). Use method + * {@link #getArtifacts()} to gain access to POM or build artifact. + * + * @see #getArtifacts() + */ + @Nonnull + Packaging getPackaging(); + + /** + * {@return the project language}. It is by default determined by {@link #getPackaging()}. + * + * @see #getPackaging() + */ + @Nonnull + default Language getLanguage() { + return getPackaging().language(); + } + + /** + * {@return the project POM artifact}, which is the artifact of the POM of this project. Every project have a POM + * artifact, even if the existence of backing POM file is NOT a requirement (i.e. for some transient projects). + * + * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact) + */ + @Nonnull + default ProducedArtifact getPomArtifact() { + return getArtifacts().get(0); + } + + /** + * {@return the project main artifact}, which is the artifact produced by this project build, if applicable. + * This artifact MAY be absent if the project is actually not producing any main artifact (i.e. "pom" packaging). + * + * @see #getPackaging() + * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact) + */ + @Nonnull + default Optional getMainArtifact() { + List artifacts = getArtifacts(); + return artifacts.size() == 2 ? Optional.of(artifacts.get(1)) : Optional.empty(); + } + + /** + * {@return the project artifacts as immutable list}. Elements are the project POM artifact and the artifact + * produced by this project build, if applicable. Hence, the returned list may have one or two elements + * (never less than 1, never more than 2), depending on project packaging. + *

+ * The list's first element is ALWAYS the project POM artifact. Presence of second element in the list depends + * solely on the project packaging. + * + * @see #getPackaging() + * @see #getPomArtifact() + * @see #getMainArtifact() + * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact) + */ + @Nonnull + List getArtifacts(); + + /** + * {@return the project model}. + */ + @Nonnull + Model getModel(); + + /** + * Shorthand method. + * + * @return the build element of the project model + */ + @Nonnull + default Build getBuild() { + Build build = getModel().getBuild(); + return build != null ? build : Build.newInstance(); + } + + /** + * Returns the path to the pom file for this project. + * A project is usually read from a file named {@code pom.xml}, + * which contains the {@linkplain #getModel() model} in an XML form. + * When a custom {@code org.apache.maven.api.spi.ModelParser} is used, + * the path may point to a non XML file. + *

+ * The POM path is also used to define the {@linkplain #getBasedir() base directory} + * of the project. + * + * @return the path of the pom + * @see #getBasedir() + */ + @Nonnull + Path getPomPath(); + + /** + * Returns the project base directory, i.e. the directory containing the project. + * A project is usually read from the file system and this will point to + * the directory containing the POM file. + * + * @return the path of the directory containing the project + */ + @Nonnull + Path getBasedir(); + + /** + * {@return the project direct dependencies (directly specified or inherited)}. + */ + @Nonnull + List getDependencies(); + + /** + * {@return the project managed dependencies (directly specified or inherited)}. + */ + @Nonnull + List getManagedDependencies(); + + /** + * {@return the project ID, usable as key}. + */ + @Nonnull + default String getId() { + return getModel().getId(); + } + + /** + * Returns a boolean indicating if the project is the top level project for + * this reactor build. The top level project may be different from the + * {@code rootDirectory}, especially if a subtree of the project is being + * built, either because Maven has been launched in a subdirectory or using + * a {@code -f} option. + * + * @return {@code true} if the project is the top level project for this build + */ + boolean isTopProject(); + + /** + * Returns a boolean indicating if the project is a root project, + * meaning that the {@link #getRootDirectory()} and {@link #getBasedir()} + * points to the same directory, and that either {@link Model#isRoot()} + * is {@code true} or that {@code basedir} contains a {@code .mvn} child + * directory. + * + * @return {@code true} if the project is the root project + * @see Model#isRoot() + */ + boolean isRootProject(); + + /** + * Gets the root directory of the project, which is the parent directory + * containing the {@code .mvn} directory or flagged with {@code root="true"}. + * + * @return the root directory of the project + * @throws IllegalStateException if the root directory could not be found + * @see Session#getRootDirectory() + */ + @Nonnull + Path getRootDirectory(); + + /** + * Returns project parent project, if any. + *

+ * Note that the model may have a parent defined, but an empty parent + * project may be returned if the parent comes from a remote repository, + * as a {@code Project} must refer to a buildable project. + * + * @return an optional containing the parent project + * @see Model#getParent() + */ + @Nonnull + Optional getParent(); + + /** + * Returns all profiles defined in this project. + *

+ * This method returns only the profiles defined directly in the current project's POM + * and does not include profiles from parent projects. + * + * @return a non-null, possibly empty list of profiles defined in this project + * @see Profile + * @see #getEffectiveProfiles() + */ + @Nonnull + List getDeclaredProfiles(); + + /** + * Returns all profiles defined in this project and all of its parent projects. + *

+ * This method traverses the parent hierarchy and includes profiles defined in parent POMs. + * The returned list contains profiles from the current project and all of its ancestors in + * the project inheritance chain. + * + * @return a non-null, possibly empty list of all profiles from this project and its parents + * @see Profile + * @see #getDeclaredProfiles() + */ + @Nonnull + List getEffectiveProfiles(); + + /** + * Returns all active profiles for the current project build. + *

+ * Active profiles are those that have been explicitly activated through one of the following means: + *

    + *
  • Command line activation using the -P flag
  • + *
  • Maven settings activation in settings.xml via <activeProfiles>
  • + *
  • Automatic activation via <activation> conditions
  • + *
  • The default active profile (marked with <activeByDefault>true</activeByDefault>)
  • + *
+ *

+ * The active profiles control various aspects of the build configuration including but not + * limited to dependencies, plugins, properties, and build resources. + * + * @return a non-null, possibly empty list of active profiles for this project + * @see Profile + * @see #getEffectiveActiveProfiles() + */ + @Nonnull + List getDeclaredActiveProfiles(); + + /** + * Returns all active profiles for this project and all of its parent projects. + *

+ * This method traverses the parent hierarchy and collects all active profiles from + * the current project and its ancestors. Active profiles are those that meet the + * activation criteria through explicit activation or automatic conditions. + *

+ * The combined set of active profiles from the entire project hierarchy affects + * the effective build configuration. + * + * @return a non-null, possibly empty list of all active profiles from this project and its parents + * @see Profile + * @see #getDeclaredActiveProfiles() + */ + @Nonnull + List getEffectiveActiveProfiles(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ProjectScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ProjectScope.java new file mode 100644 index 000000000000..1a4cd3da0873 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ProjectScope.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; + +import static org.apache.maven.api.ExtensibleEnums.projectScope; + +/** + * Project scope. + * Defines the type of source files to compile, usually either the one that compose the output package + * (i.e. the main artifact) or the ones that will be used when building tests). + *

+ * This extensible enum has two defined values, {@link #MAIN} and {@link #TEST}, + * but can be extended by registering a {@code org.apache.maven.api.spi.ProjectScopeProvider}. + *

+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface + * can be used as keys. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +@SuppressWarnings("checkstyle:InterfaceIsType") +public interface ProjectScope extends ExtensibleEnum { + + /** + * Main scope. + */ + ProjectScope MAIN = projectScope("main"); + + /** + * Test scope. + */ + ProjectScope TEST = projectScope("test"); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java new file mode 100644 index 000000000000..41300d074e63 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.annotations.ThreadSafe; + +import static java.util.Objects.requireNonNull; + +/** + * The proto session, material used to create {@link Session}. + * + * @since 4.0.0 + */ +@Experimental +@ThreadSafe +public interface ProtoSession { + + /** + * Returns immutable user properties to use for interpolation. The user properties have been configured directly + * by the user, e.g. via the {@code -Dkey=value} parameter on the command line. + * + * @return the user properties, never {@code null} + */ + @Nonnull + Map getUserProperties(); + + /** + * Returns immutable system properties to use for interpolation. The system properties are collected from the + * runtime environment such as {@link System#getProperties()} and environment variables + * (prefixed with {@code env.}). + * + * @return the system properties, never {@code null} + */ + @Nonnull + Map getSystemProperties(); + + /** + * Returns the properly overlaid map of properties: system + user. + */ + @Nonnull + Map getEffectiveProperties(); + + /** + * Returns the start time of the session. + * + * @return the start time as an Instant object, never {@code null} + */ + @Nonnull + Instant getStartTime(); + + /** + * Gets the directory of the topmost project being built, usually the current directory or the + * directory pointed at by the {@code -f/--file} command line argument. + * + * @return the directory of the topmost project, never {@code null} + * @see Project#isTopProject() + * @see #getRootDirectory() + */ + @Nonnull + Path getTopDirectory(); + + /** + * Gets the root directory of the session, which is the root directory for the top directory project. + * + * @return the root directory, never {@code null} + * @throws IllegalStateException if the root directory could not be found + * @see #getTopDirectory() + * @see Project#getRootDirectory() + * @see Project#isRootProject() + */ + @Nonnull + Path getRootDirectory(); + + /** + * Returns a proto session builder of this instance. + */ + @Nonnull + default Builder toBuilder() { + try { + return new Builder( + getUserProperties(), getSystemProperties(), getStartTime(), getTopDirectory(), getRootDirectory()); + } catch (IllegalStateException e) { + return new Builder(getUserProperties(), getSystemProperties(), getStartTime(), getTopDirectory(), null); + } + } + + /** + * Returns new builder from scratch. + */ + static Builder newBuilder() { + return new Builder().withStartTime(MonotonicClock.now()); + } + + class Builder { + private Map userProperties; + private Map systemProperties; + private Instant startTime; + private Path topDirectory; + private Path rootDirectory; + + private Builder() {} + + private Builder( + Map userProperties, + Map systemProperties, + Instant startTime, + Path topDirectory, + Path rootDirectory) { + this.userProperties = userProperties; + this.systemProperties = systemProperties; + this.startTime = startTime; + this.topDirectory = topDirectory; + this.rootDirectory = rootDirectory; + } + + public Builder withUserProperties(@Nonnull Map userProperties) { + this.userProperties = new HashMap<>(userProperties); + return this; + } + + public Builder withSystemProperties(@Nonnull Map systemProperties) { + this.systemProperties = new HashMap<>(systemProperties); + return this; + } + + public Builder withStartTime(@Nonnull Instant startTime) { + this.startTime = requireNonNull(startTime, "startTime"); + return this; + } + + public Builder withTopDirectory(@Nonnull Path topDirectory) { + this.topDirectory = requireNonNull(topDirectory, "topDirectory"); + return this; + } + + public Builder withRootDirectory(@Nullable Path rootDirectory) { + this.rootDirectory = rootDirectory; + return this; + } + + public ProtoSession build() { + return new Impl(userProperties, systemProperties, startTime, topDirectory, rootDirectory); + } + + private static class Impl implements ProtoSession { + private final Map userProperties; + private final Map systemProperties; + private final Map effectiveProperties; + private final Instant startTime; + private final Path topDirectory; + private final Path rootDirectory; + + private Impl( + Map userProperties, + Map systemProperties, + Instant startTime, + Path topDirectory, + Path rootDirectory) { + this.userProperties = Map.copyOf(userProperties); + this.systemProperties = Map.copyOf(systemProperties); + Map cp = new HashMap<>(systemProperties); + cp.putAll(userProperties); + this.effectiveProperties = Map.copyOf(cp); + this.startTime = requireNonNull(startTime); + this.topDirectory = requireNonNull(topDirectory); + this.rootDirectory = rootDirectory; + } + + @Override + public Map getUserProperties() { + return userProperties; + } + + @Override + public Map getSystemProperties() { + return systemProperties; + } + + @Override + public Map getEffectiveProperties() { + return effectiveProperties; + } + + @Override + public Instant getStartTime() { + return startTime; + } + + @Override + public Path getTopDirectory() { + return topDirectory; + } + + @Override + public Path getRootDirectory() { + if (rootDirectory == null) { + throw new IllegalStateException("root directory not set"); + } + return rootDirectory; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/RemoteRepository.java b/api/maven-api-core/src/main/java/org/apache/maven/api/RemoteRepository.java new file mode 100644 index 000000000000..a2fea88a19a8 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/RemoteRepository.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.ModelBase; + +/** + *

A remote repository is a central or distributed location + * from which Maven can download project dependencies, plugins, and other + * build artifacts. When Maven cannot find an artifact in the local + * repository, it attempts to retrieve it from one or more remote + * repositories.

+ * + *

There are several types of remote repositories:

    + *
  • Central Repository: The default remote repository used by Maven. It is a large, publicly accessible repository maintained by the Maven community at https://repo.maven.apache.org/maven2. Most common Java libraries and frameworks are hosted here.
  • + *
  • Private Remote Repository: Organizations often maintain their own private remote repositories, which may host proprietary or custom-built artifacts that are not available in the central repository. These repositories can be managed using tools like Apache Archiva, Sonatype Nexus, or JFrog Artifactory.
  • + *
  • Third-Party Repositories: Some projects or organizations host their own remote repositories for distributing specific artifacts that are not available in the central repository. These repositories must be explicitly added to the Maven pom.xml or settings.xml files for Maven to access them.
+ * + *

Repository Configuration

+ * + *

Repositories can be configured at various levels:

    + *
  1. POM: Repositories can be specified in the {@code pom.xml} file under the {@code } and {@code } sections.
  2. + *
  3. Settings: the {@code settings.xml} can be used to provide additional repositories in the three level of settings (user, project, installation).
  4. + *
+ *

By understanding and properly configuring repositories, developers can control where Maven looks for dependencies, manage access to proprietary artifacts, and optimize the build process to ensure consistency and reliability across projects. + *

+ * + * + * @since 4.0.0 + * @see Repository + * @see LocalRepository + * @see Session#getSettings() + * @see ModelBase#getRepositories() + * @see ModelBase#getPluginRepositories() + */ +@Experimental +@Immutable +public interface RemoteRepository extends Repository { + + @Nonnull + String getUrl(); + + @Nonnull + String getProtocol(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java new file mode 100644 index 000000000000..82c5a1b1c4ff --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + *

In Maven, repositories are locations where project artifacts (such as JAR files, POM files, and other + * resources) are stored and retrieved. There are two primary types of repositories: + * {@linkplain LocalRepository local repositories} and + * {@linkplain RemoteRepository remote repositories}.

+ * + *

Repository Resolution Process

+ * + *

When resolving dependencies, Maven follows this order:

    + *
  1. Check Local Repository: Maven first checks if the artifact is available in the local repository.
  2. + *
  3. Check Remote Repositories: If the artifact is not found locally, Maven queries the configured remote repositories in the order they are listed.
  4. + *
  5. Download and Cache: If Maven finds the artifact in a remote repository, it downloads it and stores it in the local repository for future use.
  6. + *
+ *

By caching artifacts in the local repository, Maven minimizes the need to repeatedly download the same artifacts, thus optimizing the build process.

+ * + *

Repository Configuration

+ * + *

Repositories can be configured at various levels:

    + *
  1. POM: Repositories can be specified in the {@code pom.xml} file under the {@code } and {@code } sections.
  2. + *
  3. Settings: the {@code settings.xml} can be used to provide additional repositories in the three level of settings (user, project, installation).
  4. + *
+ * By understanding and properly configuring repositories, developers can control where Maven looks for dependencies, manage access to proprietary artifacts, and optimize the build process to ensure consistency and reliability across projects. + * + * @since 4.0.0 + * @see RemoteRepository + * @see LocalRepository + */ +@Experimental +@Immutable +public interface Repository { + + /** + * The reserved id for Maven Central + */ + String CENTRAL_ID = "central"; + + /** + * Gets the identifier of this repository. + * + * @return the (case-sensitive) identifier, never {@code null} + */ + @Nonnull + String getId(); + + /** + * Gets the type of the repository, for example "default". + * + * @return the (case-sensitive) type of the repository, never {@code null} + */ + @Nonnull + String getType(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Service.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Service.java new file mode 100644 index 000000000000..d39bdc6766d3 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Service.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.ThreadSafe; + +/** + * Marker interface for all services provided by the {@link Session}. + *

+ * Services can be retrieved from the session using the + * {@link Session#getService(Class)} method. + * + * @since 4.0.0 + * @see Session#getService(Class) + */ +@Experimental +@ThreadSafe +public interface Service {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java new file mode 100644 index 000000000000..8ef3802062ea --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java @@ -0,0 +1,865 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.annotations.ThreadSafe; +import org.apache.maven.api.model.Repository; +import org.apache.maven.api.services.ArtifactCoordinatesFactory; +import org.apache.maven.api.services.DependencyCoordinatesFactory; +import org.apache.maven.api.services.VersionResolverException; +import org.apache.maven.api.settings.Settings; +import org.apache.maven.api.toolchain.ToolchainModel; + +/** + * The session to install / deploy / resolve artifacts and dependencies. + * + * @since 4.0.0 + */ +@Experimental +@ThreadSafe +public interface Session extends ProtoSession { + + /** + * Returns the current maven version. + * + * @return the maven version, never {@code null} + */ + @Nonnull + Version getMavenVersion(); + + /** + * Retrieves the settings for the current session. + * + * @return the settings instance + */ + @Nonnull + Settings getSettings(); + + /** + * Retrieves toolchain models that have been explicitly configured. + * + * @return the toolchain models + */ + @Nonnull + Collection getToolchains(); + + /** + * Retrieves the local repository associated with this session. + * + * @return the local repository instance + */ + @Nonnull + LocalRepository getLocalRepository(); + + /** + * Retrieves a list of remote repositories associated with this session. + * + * @return a list of remote repositories + */ + @Nonnull + List getRemoteRepositories(); + + /** + * Retrieves the session data associated with this session. + * + * @return the session data, never {@code null} + */ + @Nonnull + SessionData getData(); + + /** + * Default implementation at {@link ProtoSession} level, as the notion of project + * does not exist there. + */ + @Nonnull + default Map getEffectiveProperties() { + return getEffectiveProperties(null); + } + + /** + * Each invocation computes a new map of effective properties. To be used in interpolation. + *

+ * Effective properties are computed from system, user and optionally project properties, layered with + * defined precedence onto each other to achieve proper precedence. Precedence is defined as: + *

    + *
  • System properties (lowest precedence)
  • + *
  • Project properties (optional)
  • + *
  • User properties (highest precedence)
  • + *
+ * Note: Project properties contains properties injected from profiles, if applicable. Their precedence is + * {@code profile > project}, hence active profile property may override project property. + *

+ * The caller of this method should decide whether there is a project in scope (hence, a project instance + * needs to be passed) or not. + * + * @param project {@link Project} or {@code null}. + * @return the effective properties, never {@code null} + */ + @Nonnull + Map getEffectiveProperties(@Nullable Project project); + + /** + * Returns the degree of concurrency for the build. + * + * @return the degree of concurrency + */ + int getDegreeOfConcurrency(); + + /** + * Retrieves a list of projects associated with the session. + * + * @return a list of projects, never {@code null} + */ + @Nonnull + List getProjects(); + + /** + * Returns the plugin context for mojo being executed and the specified + * {@link Project}, never returns {@code null} as if context not present, creates it. + * + * Implementation note: while this method return type is {@link Map}, the + * returned map instance implements {@link java.util.concurrent.ConcurrentMap} as well. + * + * @throws org.apache.maven.api.services.MavenException if not called from the within a mojo execution + */ + @Nonnull + Map getPluginContext(@Nonnull Project project); + + /** + * Retrieves the service for the interface + * + * @throws NoSuchElementException if the service could not be found + */ + @Nonnull + T getService(@Nonnull Class clazz); + + /** + * Creates a derived session using the given local repository. + * + * @param localRepository the new local repository + * @return the derived session + * @throws NullPointerException if {@code localRepository} is null + */ + @Nonnull + Session withLocalRepository(@Nonnull LocalRepository localRepository); + + /** + * Creates a derived session using the given remote repositories. + * + * @param repositories the new list of remote repositories + * @return the derived session + * @throws NullPointerException if {@code repositories} is null + */ + @Nonnull + Session withRemoteRepositories(@Nonnull List repositories); + + /** + * Register the given listener which will receive all events. + * + * @param listener the listener to register + * @throws NullPointerException if {@code listener} is null + */ + void registerListener(@Nonnull Listener listener); + + /** + * Unregisters a previously registered listener. + * + * @param listener the listener to unregister + * @throws NullPointerException if {@code listener} is null + */ + void unregisterListener(@Nonnull Listener listener); + + /** + * Returns the list of registered listeners. + * + * @return an immutable collection of listeners, never {@code null} + */ + @Nonnull + Collection getListeners(); + + /** + * Shortcut for {@code getService(RepositoryFactory.class).createLocal(...)}. + * + * @param path location of the local repository to create + * @return cache of artifacts downloaded from a remote repository or built locally + * + * @see org.apache.maven.api.services.RepositoryFactory#createLocal(Path) + */ + @Nonnull + LocalRepository createLocalRepository(@Nonnull Path path); + + /** + * Shortcut for {@code getService(RepositoryFactory.class).createRemote(...)}. + * + * @param id identifier of the remote repository to create + * @param url location of the remote repository + * @return remote repository that can be used to download or upload artifacts + * + * @see org.apache.maven.api.services.RepositoryFactory#createRemote(String, String) + */ + @Nonnull + RemoteRepository createRemoteRepository(@Nonnull String id, @Nonnull String url); + + /** + * Shortcut for {@code getService(RepositoryFactory.class).createRemote(...)}. + * + * @param repository information needed for establishing connections with remote repository + * @return remote repository that can be used to download or upload artifacts + * + * @see org.apache.maven.api.services.RepositoryFactory#createRemote(Repository) + */ + @Nonnull + RemoteRepository createRemoteRepository(@Nonnull Repository repository); + + /** + * Creates a coordinates out of string that is formatted like: + * {@code :[:[:]]:}. + *

+ * Shortcut for {@code getService(ArtifactFactory.class).create(...)}. + * + * @param coordsString the string having "standard" coordinates. + * @return coordinates used to point to the artifact + * + * @see ArtifactCoordinatesFactory#create(Session, String) + */ + @Nonnull + ArtifactCoordinates createArtifactCoordinates(@Nonnull String coordsString); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).create(...)}. + * + * @param groupId the group identifier, or {@code null} is unspecified + * @param artifactId the artifact identifier, or {@code null} is unspecified + * @param version the artifact version, or {@code null} is unspecified + * @param extension the artifact extension, or {@code null} is unspecified + * @return coordinates used to point to the artifact + * + * @see ArtifactCoordinatesFactory#create(Session, String, String, String, String) + */ + @Nonnull + ArtifactCoordinates createArtifactCoordinates(String groupId, String artifactId, String version, String extension); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).create(...)}. + * + * @param groupId the group identifier, or {@code null} is unspecified + * @param artifactId the artifact identifier, or {@code null} is unspecified + * @param version the artifact version, or {@code null} is unspecified + * @param classifier the artifact classifier, or {@code null} is unspecified + * @param extension the artifact extension, or {@code null} is unspecified + * @param type the artifact type, or {@code null} is unspecified + * @return coordinates used to point to the artifact + * + * @see ArtifactCoordinatesFactory#create(Session, String, String, String, String, String, String) + */ + @Nonnull + ArtifactCoordinates createArtifactCoordinates( + String groupId, String artifactId, String version, String classifier, String extension, String type); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).create(...)}. + * + * @param artifact artifact from which to get coordinates + * @return coordinates used to point to the artifact + * + * @see ArtifactCoordinatesFactory#create(Session, String, String, String, String, String, String) + */ + @Nonnull + ArtifactCoordinates createArtifactCoordinates(@Nonnull Artifact artifact); + + /** + * Shortcut for {@code getService(DependencyFactory.class).create(...)}. + * + * @param coordinates artifact coordinates to get as a dependency coordinates + * @return dependency coordinates for the given artifact + * + * @see DependencyCoordinatesFactory#create(Session, ArtifactCoordinates) + */ + @Nonnull + DependencyCoordinates createDependencyCoordinates(@Nonnull ArtifactCoordinates coordinates); + + /** + * Shortcut for {@code getService(DependencyFactory.class).create(...)}. + * + * @param dependency dependency for which to get the coordinates + * @return coordinates for the given dependency + * + * @see DependencyCoordinatesFactory#create(Session, Dependency) + */ + @Nonnull + DependencyCoordinates createDependencyCoordinates(@Nonnull Dependency dependency); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).create(...)}. + * + * @param groupId the group identifier, or {@code null} is unspecified + * @param artifactId the artifact identifier, or {@code null} is unspecified + * @param version the artifact version, or {@code null} is unspecified + * @param extension the artifact extension, or {@code null} is unspecified + * @return artifact with the given coordinates + * + * @see org.apache.maven.api.services.ArtifactFactory#create(Session, String, String, String, String) + */ + @Nonnull + Artifact createArtifact(String groupId, String artifactId, String version, String extension); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).create(...)}. + * + * @param groupId the group identifier, or {@code null} is unspecified + * @param artifactId the artifact identifier, or {@code null} is unspecified + * @param version the artifact version, or {@code null} is unspecified + * @param classifier the artifact classifier, or {@code null} is unspecified + * @param extension the artifact extension, or {@code null} is unspecified + * @param type the artifact type, or {@code null} is unspecified + * @return artifact with the given coordinates + * + * @see org.apache.maven.api.services.ArtifactFactory#create(Session, String, String, String, String, String, String) + */ + @Nonnull + Artifact createArtifact( + String groupId, String artifactId, String version, String classifier, String extension, String type); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).createProduced(...)}. + * + * @param groupId the group identifier, or {@code null} is unspecified + * @param artifactId the artifact identifier, or {@code null} is unspecified + * @param version the artifact version, or {@code null} is unspecified + * @param extension the artifact extension, or {@code null} is unspecified + * @return artifact with the given coordinates + * + * @see org.apache.maven.api.services.ArtifactFactory#createProduced(Session, String, String, String, String) + */ + @Nonnull + ProducedArtifact createProducedArtifact(String groupId, String artifactId, String version, String extension); + + /** + * Shortcut for {@code getService(ArtifactFactory.class).createProduced(...)}. + * + * @param groupId the group identifier, or {@code null} is unspecified + * @param artifactId the artifact identifier, or {@code null} is unspecified + * @param version the artifact version, or {@code null} is unspecified + * @param classifier the artifact classifier, or {@code null} is unspecified + * @param extension the artifact extension, or {@code null} is unspecified + * @param type the artifact type, or {@code null} is unspecified + * @return artifact with the given coordinates + * + * @see org.apache.maven.api.services.ArtifactFactory#createProduced(Session, String, String, String, String, String, String) + */ + @Nonnull + ProducedArtifact createProducedArtifact( + String groupId, String artifactId, String version, String classifier, String extension, String type); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param coordinates coordinates of the artifact to resolve + * @return requested artifact together with the path to its file + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + DownloadedArtifact resolveArtifact(@Nonnull ArtifactCoordinates coordinates); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param coordinates coordinates of the artifact to resolve + * @param repositories repositories to use, if {@code null}, the session repositories are used + * @return requested artifact together with the path to its file + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + DownloadedArtifact resolveArtifact(@Nonnull ArtifactCoordinates coordinates, List repositories); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param coordinates coordinates of all artifacts to resolve + * @return requested artifacts together with the paths to their files + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + Collection resolveArtifacts(@Nonnull ArtifactCoordinates... coordinates); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param coordinates coordinates of all artifacts to resolve + * @return requested artifacts together with the paths to their files + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + Collection resolveArtifacts(@Nonnull Collection coordinates); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param coordinates coordinates of all artifacts to resolve + * @param repositories repositories to use, if {@code null}, the session repositories are used + * @return requested artifacts together with the paths to their files + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + Collection resolveArtifacts( + @Nonnull Collection coordinates, + @Nullable List repositories); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param artifact the artifact to resolve + * @return requested artifact together with the path to its file + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + DownloadedArtifact resolveArtifact(@Nonnull Artifact artifact); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param artifact the artifact to resolve + * @param repositories repositories to use, if {@code null}, the session repositories are used + * @return requested artifact together with the path to its file + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + DownloadedArtifact resolveArtifact(@Nonnull Artifact artifact, @Nullable List repositories); + + /** + * Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}. + * + * @param artifacts all artifacts to resolve + * @return requested artifacts together with the paths to their files + * @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed + * + * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection) + */ + @Nonnull + Collection resolveArtifacts(@Nonnull Artifact... artifacts); + + /** + * Shortcut for {@code getService(ArtifactInstaller.class).install(...)}. + * + * @param artifacts the artifacts to install + * @throws org.apache.maven.api.services.ArtifactInstallerException if the artifacts installation failed + * + * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection) + */ + void installArtifacts(@Nonnull ProducedArtifact... artifacts); + + /** + * Shortcut for {@code getService(ArtifactInstaller.class).install(...)}. + * + * @param artifacts the artifacts to install + * @throws org.apache.maven.api.services.ArtifactInstallerException if the artifacts installation failed + * + * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection) + */ + void installArtifacts(@Nonnull Collection artifacts); + + /** + * Shortcut for {@code getService(ArtifactDeployer.class).deploy(...)}. + * + * @param repository the repository where to deploy artifacts + * @param artifacts the artifacts to deploy + * @throws org.apache.maven.api.services.ArtifactDeployerException if the artifacts deployment failed + * + * @see org.apache.maven.api.services.ArtifactDeployer#deploy(Session, RemoteRepository, Collection) + */ + void deployArtifact(@Nonnull RemoteRepository repository, @Nonnull ProducedArtifact... artifacts); + + /** + * Shortcut for {@code getService(ArtifactManager.class).setPath(...)}. + * + * @param artifact the artifact for which to associate a path + * @param path path to associate to the given artifact + * + * @see org.apache.maven.api.services.ArtifactManager#setPath(ProducedArtifact, Path) + */ + void setArtifactPath(@Nonnull ProducedArtifact artifact, @Nonnull Path path); + + /** + * Shortcut for {@code getService(ArtifactManager.class).getPath(...)}. + * + * @param artifact the artifact for which to get a path + * @return path associated to the given artifact + * + * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact) + */ + @Nonnull + Optional getArtifactPath(@Nonnull Artifact artifact); + + /** + * Gets the relative path for a locally installed artifact. Note that the artifact need not actually exist yet at + * the returned location, the path merely indicates where the artifact would eventually be stored. + *

+ * Shortcut for {@code getService(LocalArtifactManager.class).getPathForLocalArtitact(...)}. + * + * @param artifact the artifact for which to get a local path + * @return local path associated to the given artifact, or {@code null} if none + * + * @see org.apache.maven.api.services.LocalRepositoryManager#getPathForLocalArtifact(Session, LocalRepository, Artifact) + */ + Path getPathForLocalArtifact(@Nonnull Artifact artifact); + + /** + * Gets the relative path for an artifact cached from a remote repository. + * Note that the artifact need not actually exist yet at the returned location, + * the path merely indicates where the artifact would eventually be stored. + *

+ * Shortcut for {@code getService(LocalArtifactManager.class).getPathForRemoteArtifact(...)}. + * + * @param remote the repository from where artifacts are downloaded + * @param artifact the artifact for which to get a path + * @return path associated to the given artifact + * + * @see org.apache.maven.api.services.LocalRepositoryManager#getPathForRemoteArtifact(Session, LocalRepository, RemoteRepository, Artifact) + */ + @Nonnull + Path getPathForRemoteArtifact(@Nonnull RemoteRepository remote, @Nonnull Artifact artifact); + + /** + * Checks whether a given artifact version is considered a {@code SNAPSHOT} or not. + *

+ * Shortcut for {@code getService(ArtifactManager.class).isSnapshot(...)}. + *

+ * In case there is {@link Artifact} in scope, the recommended way to perform this check is + * use of {@link Artifact#isSnapshot()} instead. + * + * @param version artifact version + * @return whether the given version is a snapshot + * + * @see org.apache.maven.api.services.VersionParser#isSnapshot(String) + */ + boolean isVersionSnapshot(@Nonnull String version); + + /** + * Shortcut for {@code getService(DependencyResolver.class).collect(...)} + * + * @param artifact artifact for which to get the dependencies, including transitive ones + * @param scope the {link PathScope} to collect dependencies, must not be {@code null} + * @return root node of the dependency graph for the given artifact + * + * @see org.apache.maven.api.services.DependencyResolver#collect(Session, Artifact, PathScope) + * @throws org.apache.maven.api.services.DependencyResolverException if the dependency collection failed + */ + @Nonnull + Node collectDependencies(@Nonnull Artifact artifact, @Nonnull PathScope scope); + + /** + * Shortcut for {@code getService(DependencyResolver.class).collect(...)} + * + * @param project project for which to get the dependencies, including transitive ones + * @param scope the {link PathScope} to collect dependencies, must not be {@code null} + * @return root node of the dependency graph for the given project + * + * @see org.apache.maven.api.services.DependencyResolver#collect(Session, Project, PathScope) + * @throws org.apache.maven.api.services.DependencyResolverException if the dependency collection failed + */ + @Nonnull + Node collectDependencies(@Nonnull Project project, @Nonnull PathScope scope); + + /** + * Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is + * only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the + * artifact files. + *

+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...)} + * + * @param dependency dependency for which to get transitive dependencies + * @param scope the {link PathScope} to collect dependencies, must not be {@code null} + * @return root node of the dependency graph for the given artifact + * + * @see org.apache.maven.api.services.DependencyResolver#collect(Session, DependencyCoordinates, PathScope) + * @throws org.apache.maven.api.services.DependencyResolverException if the dependency collection failed + */ + @Nonnull + Node collectDependencies(@Nonnull DependencyCoordinates dependency, @Nonnull PathScope scope); + + /** + * Shortcut for {@code getService(DependencyResolver.class).flatten(...)}. + * + * @param node node for which to get a flattened list + * @param scope build path scope (main compile, test compile, etc.) of desired nodes + * @return flattened list of node with the given build path scope + * @throws org.apache.maven.api.services.DependencyResolverException if the dependency flattening failed + * + * @see org.apache.maven.api.services.DependencyResolver#flatten(Session, Node, PathScope) + */ + @Nonnull + List flattenDependencies(@Nonnull Node node, @Nonnull PathScope scope); + + /** + * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}. + * + * @param dependencyCoordinates coordinates of the dependency for which to get the paths + * @return paths to the transitive dependencies of the given dependency + * + * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, DependencyCoordinates) + */ + @Nonnull + List resolveDependencies(@Nonnull DependencyCoordinates dependencyCoordinates); + + /** + * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}. + * + * @param dependencyCoordinates coordinates of all dependency for which to get the paths + * @return paths to the transitive dependencies of the given dependencies + * + * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, List) + */ + @Nonnull + List resolveDependencies(@Nonnull List dependencyCoordinates); + + /** + * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}. + * + * @param project the project for which to get dependencies + * @param scope build path scope (main compile, test compile, etc.) of desired paths + * @return paths to the transitive dependencies of the given project + * + * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope) + */ + @Nonnull + List resolveDependencies(@Nonnull Project project, @Nonnull PathScope scope); + + /** + * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getDispatchedPaths()}. + * + * @param dependencyCoordinates coordinates of the dependency for which to get the paths + * @param scope build path scope (main compile, test compile, etc.) of desired paths + * @param desiredTypes the type of paths to include in the result + * @return paths to the transitive dependencies of the given project + * + * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope) + */ + @Nonnull + Map> resolveDependencies( + @Nonnull DependencyCoordinates dependencyCoordinates, + @Nonnull PathScope scope, + @Nonnull Collection desiredTypes); + + /** + * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getDispatchedPaths()}. + * + * @param project the project for which to get dependencies + * @param scope build path scope (main compile, test compile, etc.) of desired paths + * @param desiredTypes the type of paths to include in the result + * @return paths to the transitive dependencies of the given project + * + * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope) + */ + @Nonnull + Map> resolveDependencies( + @Nonnull Project project, @Nonnull PathScope scope, @Nonnull Collection desiredTypes); + + /** + * Resolves an artifact's meta version (if any) to a concrete version. + * For example, resolves "1.0-SNAPSHOT" to "1.0-20090208.132618-23". + *

+ * Shortcut for {@code getService(VersionResolver.class).resolve(...)} + * + * @param artifact the artifact for which to resolve the version + * @return resolved version of the given artifact + * @throws org.apache.maven.api.services.VersionResolverException if the resolution failed + * + * @see org.apache.maven.api.services.VersionResolver#resolve(Session, ArtifactCoordinates) (String) + */ + @Nonnull + Version resolveVersion(@Nonnull ArtifactCoordinates artifact) throws VersionResolverException; + + /** + * Expands a version range to a list of matching versions, in ascending order. + * For example, resolves "[3.8,4.0)" to "3.8", "3.8.1", "3.8.2". + * The returned list of versions is only dependent on the configured repositories and their contents. + * The supplied request may also refer to a single concrete version rather than a version range. + * In this case though, the result contains simply the (parsed) input version, regardless of the + * repositories and their contents. + * + * @param artifact the artifact for which to resolve the versions + * @return a list of resolved {@code Version}s. + * @throws org.apache.maven.api.services.VersionRangeResolverException if the resolution failed + * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinates) (String) + */ + @Nonnull + List resolveVersionRange(@Nonnull ArtifactCoordinates artifact) throws VersionResolverException; + + /** + * Expands a version range to a list of matching versions, in ascending order. + * For example, resolves "[3.8,4.0)" to "3.8", "3.8.1", "3.8.2". + * The returned list of versions is only dependent on the configured repositories and their contents. + * The supplied request may also refer to a single concrete version rather than a version range. + * In this case though, the result contains simply the (parsed) input version, regardless of the + * repositories and their contents. + * + * @param artifact the artifact for which to resolve the versions + * @param repositories the repositories to use, or the session repositories if {@code null} + * @return a list of resolved {@code Version}s. + * @throws org.apache.maven.api.services.VersionRangeResolverException if the resolution failed + * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinates) (String) + */ + @Nonnull + List resolveVersionRange(@Nonnull ArtifactCoordinates artifact, List repositories) + throws VersionResolverException; + + /** + * Resolves the highest available version of a version range. + * The returned version is only dependent on the configured repositories and their contents. + * The supplied request may also refer to a single concrete version rather than a version range. + * In this case though, the result contains simply the (parsed) input version, regardless of the + * repositories and their contents. + * + * @param artifact the artifact for which to resolve the versions + * @param repositories the repositories to use, or the session repositories if {@code null} + * @return the highest resolved {@code Version}. + * @throws org.apache.maven.api.services.VersionRangeResolverException if the resolution failed + * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinates) (String) + */ + @Nonnull + Optional resolveHighestVersion(@Nonnull ArtifactCoordinates artifact, List repositories) + throws VersionResolverException; + + /** + * Parses the specified version string, for example "1.0". + *

+ * Shortcut for {@code getService(VersionParser.class).parseVersion(...)}. + * + * @param version the version string to parse + * @return the version parsed from the given string + * @throws org.apache.maven.api.services.VersionParserException if the parsing failed + * @see org.apache.maven.api.services.VersionParser#parseVersion(String) + */ + @Nonnull + Version parseVersion(@Nonnull String version); + + /** + * Parses the specified version range specification, for example "[1.0,2.0)". + *

+ * Shortcut for {@code getService(VersionParser.class).parseVersionRange(...)}. + * + * @param versionRange the version string to parse + * @return the version range parsed from the given string + * @throws org.apache.maven.api.services.VersionParserException if the parsing failed + * @see org.apache.maven.api.services.VersionParser#parseVersionRange(String) + */ + @Nonnull + VersionRange parseVersionRange(@Nonnull String versionRange); + + /** + * Parses the specified version constraint specification, for example "1.0" or "[1.0,2.0)". + *

+ * Shortcut for {@code getService(VersionParser.class).parseVersionConstraint(...)}. + * + * @param versionConstraint the version string to parse + * @return the version constraint parsed from the given string + * @throws org.apache.maven.api.services.VersionParserException if the parsing failed + * @see org.apache.maven.api.services.VersionParser#parseVersionConstraint(String) + */ + @Nonnull + VersionConstraint parseVersionConstraint(@Nonnull String versionConstraint); + + /** + * Obtain the {@link Type} from the specified {@code id}. + *

+ * Shortcut for {@code getService(TypeRegistry.class).require(...)}. + * + * @see org.apache.maven.api.services.TypeRegistry#require(String) + */ + @Nonnull + Type requireType(@Nonnull String id); + + /** + * Obtain the {@link Language} from the specified {@code id}. + *

+ * Shortcut for {@code getService(LanguageRegistry.class).require(...)}. + * + * @see org.apache.maven.api.services.LanguageRegistry#require(String) + */ + @Nonnull + Language requireLanguage(@Nonnull String id); + + /** + * Obtain the {@link Packaging} from the specified {@code id}. + *

+ * Shortcut for {@code getService(PackagingRegistry.class).require(...)}. + * + * @see org.apache.maven.api.services.PackagingRegistry#require(String) + */ + @Nonnull + Packaging requirePackaging(@Nonnull String id); + + /** + * Obtain the {@link ProjectScope} from the specified {@code id}. + *

+ * Shortcut for {@code getService(ProjectScopeRegistry.class).require(...)}. + * + * @see org.apache.maven.api.services.ProjectScopeRegistry#require(String) + */ + @Nonnull + ProjectScope requireProjectScope(@Nonnull String id); + + /** + * Obtain the {@link DependencyScope} from the specified {@code id}. + *

+ * Shortcut for {@code DependencyScope.forId(...)} with a verification that the given identifier exists. + * + * @param id the identifier of the scope (case-sensitive) + * @return the scope for the given identifier (never null) + * @throws IllegalArgumentException if the given identifier is not a known scope + * + * @see org.apache.maven.api.DependencyScope#forId(String) + */ + @Nonnull + DependencyScope requireDependencyScope(@Nonnull String id); + + /** + * Obtain the {@link PathScope} from the specified {@code id}. + *

+ * Shortcut for {@code getService(PathScopeRegistry.class).require(...)}. + * + * @see org.apache.maven.api.services.PathScopeRegistry#require(String) + */ + @Nonnull + PathScope requirePathScope(@Nonnull String id); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/SessionData.java b/api/maven-api-core/src/main/java/org/apache/maven/api/SessionData.java new file mode 100644 index 000000000000..5afb04795a9f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/SessionData.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.annotations.Provider; +import org.apache.maven.api.annotations.ThreadSafe; + +/** + * A container for data that is specific to a session. + * All components may use this storage to associate arbitrary data with a session. + *

+ * Unlike a cache, this session data is not subject to purging. For this same reason, session data should also not be + * abused as a cache (i.e. for storing values that can be re-calculated) to avoid memory exhaustion. + *

+ * Note: Actual implementations must be thread-safe. + * + * @see Session#getData() + * @since 4.0.0 + */ +@Experimental +@ThreadSafe +@Provider +public interface SessionData { + + /** + * Associates the specified session data with the given key. + * + * @param key the key under which to store the session data, must not be {@code null} + * @param value the data to associate with the key, may be {@code null} to remove the mapping + */ + void set(@Nonnull Key key, @Nullable T value); + + /** + * Associates the specified session data with the given key if the key is currently mapped to the given value. This + * method provides an atomic compare-and-update of some key's value. + * + * @param key the key under which to store the session data, must not be {@code null} + * @param oldValue the expected data currently associated with the key, may be {@code null} + * @param newValue the data to associate with the key, may be {@code null} to remove the mapping + * @return {@code true} if the key mapping was successfully updated from the old value to the new value, + * {@code false} if the current key mapping didn't match the expected value and was not updated. + */ + boolean replace(@Nonnull Key key, @Nullable T oldValue, @Nullable T newValue); + + /** + * Gets the session data associated with the specified key. + * + * @param key the key for which to retrieve the session data, must not be {@code null} + * @return the session data associated with the key or {@code null} if none + */ + @Nullable + T get(@Nonnull Key key); + + /** + * Retrieve of compute the data associated with the specified key. + * + * @param key the key for which to retrieve the session data, must not be {@code null} + * @param supplier the supplier will compute the new value + * @return the session data associated with the key + */ + @Nullable + T computeIfAbsent(@Nonnull Key key, @Nonnull Supplier supplier); + + /** + * Create a key using the given class as an identifier and as the type of the object. + */ + static Key key(Class clazz) { + return new Key<>(clazz, clazz); + } + + /** + * Create a key using the given class and id. + */ + static Key key(Class clazz, Object id) { + return new Key<>(clazz, id); + } + + /** + * Key used to query the session data + * @param the type of the object associated to this key + */ + final class Key { + + private final Class type; + private final Object id; + + private Key(Class type, Object id) { + this.type = type; + this.id = id; + } + + public Class type() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Key key = (Key) o; + return Objects.equals(id, key.id) && Objects.equals(type, key.type); + } + + @Override + public int hashCode() { + return Objects.hash(id, type); + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java b/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java new file mode 100644 index 000000000000..4a4aa4f561aa --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * A root directory of source files. + * The sources may be Java main classes, test classes, resources or anything else identified by the scope. + * + *

Default values

+ * The properties in this interface are defined in the {@code } element of the + * {@linkplain org.apache.maven.api.model.Model Maven project descriptor}. + * For each property, the default value is either empty or documented in the project descriptor. + */ +public interface SourceRoot { + /** + * {@return the root directory where the sources are stored}. + * The path is relative to the POM file. + * + *

Default implementation

+ * The default value depends on whether a {@linkplain #module() module name} is specified in this source root: + *
    + *
  • + * If no module name, then the default directory is + * src/{@linkplain #scope() scope}/{@linkplain #language() language}. + *
  • + * If a module name is present, then the default directory is + * src/{@linkplain #module() module}/{@linkplain #scope() scope}/{@linkplain #language() language}. + *
  • + *
+ * + * The default value is relative. + * Implementation may override with absolute path instead. + */ + default Path directory() { + Path src = Path.of("src"); + return module().map(src::resolve) + .orElse(src) + .resolve(scope().id()) + .resolve(language().id()); + } + + /** + * {@return the list of patterns for the files to include}. + * The path separator is {@code /} on all platforms, including Windows. + * The prefix before the {@code :} character, if present and longer than 1 character, is the syntax. + * If no syntax is specified, or if its length is 1 character (interpreted as a Windows drive), + * the default is a Maven-specific variation of the {@code "glob"} pattern. + * + *

The default implementation returns an empty list, which means to apply a language-dependent pattern. + * For example, for the Java language, the pattern includes all files with the {@code .java} suffix.

+ * + * @see java.nio.file.FileSystem#getPathMatcher(String) + */ + default List includes() { + return List.of(); + } + + /** + * {@return the list of patterns for the files to exclude}. + * The exclusions are applied after the inclusions. + * The default implementation returns an empty list. + */ + default List excludes() { + return List.of(); + } + + /** + * {@return a matcher combining the include and exclude patterns}. + * If the user did not specify any includes, the given {@code defaultIncludes} are used. + * These defaults depend on the plugin. + * For example, the default include of the Java compiler plugin is "**/*.java". + * + *

If the user did not specify any excludes, the default is often files generated + * by Source Code Management (SCM) software or by the operating system. + * Examples: "**/.gitignore", "**/.DS_Store".

+ * + * @param defaultIncludes the default includes if unspecified by the user + * @param useDefaultExcludes whether to add the default set of patterns to exclude, + * mostly Source Code Management (SCM) files + */ + PathMatcher matcher(Collection defaultIncludes, boolean useDefaultExcludes); + + /** + * {@return in which context the source files will be used}. + * Not to be confused with dependency scope. + * The default value is {@code "main"}. + * + * @see ProjectScope#MAIN + */ + default ProjectScope scope() { + return ProjectScope.MAIN; + } + + /** + * {@return the language of the source files}. + * The default value is {@code "java"}. + * + * @see Language#JAVA_FAMILY + */ + default Language language() { + return Language.JAVA_FAMILY; + } + + /** + * {@return the name of the Java module (or other language-specific module) which is built by the sources}. + * The default value is empty. + */ + default Optional module() { + return Optional.empty(); + } + + /** + * {@return the version of the platform where the code will be executed}. + * In a Java environment, this is the value of the {@code --release} compiler option. + * The default value is empty. + */ + default Optional targetVersion() { + return Optional.empty(); + } + + /** + * {@return an explicit target path, overriding the default value}. + * When a target path is explicitly specified, the values of the {@link #module()} and {@link #targetVersion()} + * elements are not used for inferring the path (they are still used as compiler options however). + * It means that for scripts and resources, the files below the path specified by {@link #directory()} + * are copied to the path specified by {@code targetPath()} with the exact same directory structure. + */ + default Optional targetPath() { + return Optional.empty(); + } + + /** + * {@return whether resources are filtered to replace tokens with parameterized values}. + * The default value is {@code false}. + */ + default boolean stringFiltering() { + return false; + } + + /** + * {@return whether the directory described by this source element should be included in the build}. + * The default value is {@code true}. + */ + default boolean enabled() { + return true; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Toolchain.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Toolchain.java new file mode 100644 index 000000000000..dcbe72ea4ade --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Toolchain.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Map; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.toolchain.ToolchainModel; + +/** + * Represents a toolchain in the Maven build system. + * + *

A toolchain is a set of tools that can be used to build a project. + * This interface allows users to define and configure various toolchains + * that can be utilized by Maven during the build process. Toolchains can + * include compilers, interpreters, and other tools that are necessary + * for building a project in a specific environment.

+ * + *

Toolchains are defined in the Maven toolchains.xml file and can be + * referenced in the project's POM file. This allows for greater flexibility + * and control over the build environment, enabling developers to specify + * the exact versions of tools they wish to use.

+ * + *

+ * Toolchains can be obtained through the {@link org.apache.maven.api.services.ToolchainManager ToolchainManager} + * service. This service provides methods to retrieve and manage toolchains defined + * in the Maven configuration. + *

+ * + *

+ * The following are key functionalities provided by the Toolchain interface:

    + *
  • Access to the type of the toolchain (e.g., JDK, compiler).
  • + *
  • Retrieval of the specific version of the toolchain.
  • + *
  • Configuration of toolchain properties to match the project's requirements.
  • + *
+ * + *

Example usage:

+ *
+ * Toolchain toolchain = ...; // Obtain a Toolchain instance
+ * String type = toolchain.getType(); // Get the type of the toolchain
+ * String version = toolchain.getVersion(); // Get the version of the toolchain
+ * 
+ * + * + * @since 4.0.0 + * @see JavaToolchain + * @see org.apache.maven.api.services.ToolchainManager + */ +@Experimental +public interface Toolchain { + /** + * Gets the type of toolchain. + * + * @return the toolchain type + */ + String getType(); + + /** + * Gets the underlying toolchain model. + * + * @return the toolchain model + */ + ToolchainModel getModel(); + + /** + * Gets the platform tool executable. + * + * @param toolName the tool platform independent tool name + * @return file representing the tool executable, or null if the tool cannot be found + */ + String findTool(String toolName); + + /** + * Let the toolchain decide if it matches requirements defined + * in the toolchain plugin configuration. + * + * @param requirements key value pair, may not be {@code null} + * @return {@code true} if the requirements match, otherwise {@code false} + */ + boolean matchesRequirements(Map requirements); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java new file mode 100644 index 000000000000..98861483ff42 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.util.Set; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.model.Dependency; + +/** + * A dependency's {@code Type} is uniquely identified by a {@code String}, + * and semantically represents a known kind of dependency. + *

+ * It provides information about the file type (or extension) of the associated artifact, + * its default classifier, and how the artifact will be used in the build when creating + * class paths or module paths. + *

+ * For example, the type {@code java-source} has a {@code jar} extension and a + * {@code sources} classifier. The artifact and its dependencies should be added + * to the build path. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Type extends ExtensibleEnum { + /** + * Artifact type name for a POM file. + */ + String POM = "pom"; + + /** + * Artifact type name for a BOM file. + */ + String BOM = "bom"; + + /** + * Artifact type name for a JAR file that can be placed either on the class path or on the module path. + * The path (classes or modules) is chosen by the plugin, possibly using heuristic rules. + * This is the behavior of Maven 3. + */ + String JAR = "jar"; + + /** + * Artifact type name for a fat-JAR file that can be only on the class path. + * The fat-JAR is a self-contained JAR and its transitive dependencies will not be resolved, if any. + * This type is new in Maven 4. + */ + String FATJAR = "fatjar"; + + /** + * Artifact type name for a JAR file to unconditionally place on the class path. + * If the JAR is modular, its module information are ignored. + * This type is new in Maven 4. + */ + String CLASSPATH_JAR = "classpath-jar"; + + /** + * Artifact type name for a JAR file to unconditionally place on the module path. + * If the JAR is not modular, then it is loaded by Java as an unnamed module. + * This type is new in Maven 4. + */ + String MODULAR_JAR = "modular-jar"; + + /** + * Artifact type name for a JAR file that can be placed either on the annotation processor class path + * or module path. The path (classes or modules) is chosen by the plugin, possibly using heuristic rules. + */ + String PROCESSOR = "processor"; + + /** + * Artifact type name for a JAR file to unconditionally place on the annotation processor class path. + * If the JAR is modular, its module information are ignored. + */ + String CLASSPATH_PROCESSOR = "classpath-processor"; + + /** + * Artifact type name for a JAR file to unconditionally place on the annotation processor module path. + * If the JAR is not modular, then it is loaded by Java as an unnamed module. + */ + String MODULAR_PROCESSOR = "modular-processor"; + + /** + * Artifact type name for source code packaged in a JAR file. + */ + String JAVA_SOURCE = "java-source"; + + /** + * Artifact type name for javadoc packaged in a JAR file. + */ + String JAVADOC = "javadoc"; + + /** + * Artifact type name for a Maven plugin. + */ + String MAVEN_PLUGIN = "maven-plugin"; + + /** + * Artifact type name for a JAR file containing test classes. If the main artifact is placed on the class path + * ({@value #JAR} or {@value #CLASSPATH_JAR} types), then the test artifact will also be placed on the class path. + * Otherwise, if the main artifact is placed on the module path ({@value #JAR} or {@value #MODULAR_JAR} types), + * then the test artifact will be added using {@code --patch-module} option. + */ + String TEST_JAR = "test-jar"; + + /** + * Artifact type name for a JAR file containing test sources. + */ + String TEST_JAVA_SOURCE = "test-java-source"; + + /** + * Returns the dependency type id. + * The id uniquely identifies this dependency type. + * + * @return the id of this type, never {@code null}. + */ + @Nonnull + @Override + String id(); + + /** + * Returns the dependency type language. + * + * @return the language of this type, never {@code null}. + */ + @Nonnull + Language getLanguage(); + + /** + * Get the file extension of artifacts of this type. + * + * @return the file extension, never {@code null}. + */ + @Nonnull + String getExtension(); + + /** + * Get the default classifier associated to the dependency type. + * The default classifier can be overridden when specifying + * the {@link Dependency#getClassifier()}. + * + * @return the default classifier, or {@code null}. + */ + @Nullable + String getClassifier(); + + /** + * Specifies if the artifact already embeds its own dependencies. + * This is the case for JEE packages or similar artifacts such as + * WARs, EARs, etc. + * + * @return if the artifact's dependencies are included in the artifact + */ + boolean isIncludesDependencies(); + + /** + * Types of path (class-path, module-path, …) where the dependency can be placed. + * For most deterministic builds, the array length should be 1. In such case, + * the dependency will be unconditionally placed on the specified type of path + * and no heuristic rule will be involved. + * + *

It is nevertheless common to specify two or more types of path. For example, + * a Java library may be compatible with either the class path or the module path, + * and the user may have provided no instruction about which type to use. In such + * case, the plugin may apply rules for choosing a path. See for example + * {@link JavaPathType#CLASSES} and {@link JavaPathType#MODULES}.

+ */ + @Nonnull + Set getPathTypes(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java new file mode 100644 index 000000000000..6d533ecfb601 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * A version or meta-version of an artifact or a dependency. + * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. + * Versions are usually parsed using the {@link org.apache.maven.api.services.VersionParser} service. + * + * @since 4.0.0 + * @see org.apache.maven.api.services.VersionParser#parseVersion(String) + * @see org.apache.maven.api.Session#parseVersion(String) + * @see VersionConstraint + * @see VersionRange + */ +@Experimental +public interface Version extends Comparable { + /** + * {@return the string representation of this version} + */ + @Nonnull + @Override + String toString(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/VersionConstraint.java b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionConstraint.java new file mode 100644 index 000000000000..5d5ac0fc4f80 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionConstraint.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Version constraint for dependency. + * Constraint is either a range ("[1,2)") or recommended version ("1.0"). + * + * {@code VersionConstraint} objects are created using the + * {@linkplain org.apache.maven.api.services.VersionParser} service. + * + * @see Version + * @see VersionRange + * @see org.apache.maven.api.services.VersionParser#parseVersionConstraint(String) + * @see org.apache.maven.api.Session#parseVersionConstraint(String) + * @since 4.0.0 + */ +@Experimental +public interface VersionConstraint { + /** + * Returns the range of this constraint, or {@code null} if none. + *

+ * Note: only one, this method or {@link #getRecommendedVersion()} method must return non-{@code null} value. + */ + @Nullable + VersionRange getVersionRange(); + + /** + * Returns the recommended version of this constraint, or {@code null} if none. + *

+ * Note: only one, this method or {@link #getVersionRange()} method must return non-{@code null} value. + */ + @Nullable + Version getRecommendedVersion(); + + /** + * Determines whether the specified version is contained within this constraint. + * + * @param version the version to test, must not be {@code null} + * @return {@code true} if this range contains the specified version, {@code false} otherwise + */ + boolean contains(@Nonnull Version version); + + /** + * {@return the string representation of this version} + */ + @Nonnull + @Override + String toString(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java new file mode 100644 index 000000000000..224b5d6f75fb --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * A range of versions. + * + * {@code VersionConstraint} objects are created using the + * {@linkplain org.apache.maven.api.services.VersionParser} service. + * + * @see Version + * @see VersionConstraint + * @see org.apache.maven.api.services.VersionParser#parseVersionRange(String) + * @see org.apache.maven.api.Session#parseVersionRange(String) + * @since 4.0.0 + */ +@Experimental +public interface VersionRange { + /** + * Determines whether the specified version is contained within this range. + * + * @param version the version to test, must not be {@code null} + * @return {@code true} if this range contains the specified version, {@code false} otherwise + */ + boolean contains(@Nonnull Version version); + + /** + * Returns the upper boundary of this range, or {@code null} if none. + */ + @Nullable + Boundary getUpperBoundary(); + + /** + * Returns the lower boundary of this range, or {@code null} if none. + */ + @Nullable + Boundary getLowerBoundary(); + + /** + * {@return the string representation of this version} + */ + @Nonnull + @Override + String toString(); + + /** + * Represents range boundary. + */ + interface Boundary { + /** + * The bounding version. + */ + Version getVersion(); + + /** + * Returns {@code true} if version is included of the range. + */ + boolean isInclusive(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/WorkspaceRepository.java b/api/maven-api-core/src/main/java/org/apache/maven/api/WorkspaceRepository.java new file mode 100644 index 000000000000..f8cd2bac6d3f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/WorkspaceRepository.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import org.apache.maven.api.annotations.Nonnull; + +/** + * Represents a repository backed by an IDE workspace, the output of a build session, + * or similar ad-hoc collections of artifacts. This repository is considered read-only + * within the context of a session, meaning it can only be used for artifact resolution, + * not for installation or deployment. This interface does not provide direct access + * to artifacts; that functionality is handled by a {@code WorkspaceReader}. + */ +public interface WorkspaceRepository extends Repository { + + /** + * {@return the type of the repository, i.e. "workspace"} + */ + @Nonnull + @Override + default String getType() { + return "workspace"; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/BatchRequestException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/BatchRequestException.java new file mode 100644 index 000000000000..4757d60c7abb --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/BatchRequestException.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import java.util.List; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.Request; +import org.apache.maven.api.services.Result; + +/** + * Exception thrown when a batch request operation fails. This exception contains the results + * of all requests that were attempted, including both successful and failed operations. + *

+ * The exception provides access to detailed results through {@link #getResults()}, allowing + * callers to determine which specific requests failed and why. + * + * @since 4.0.0 + */ +@Experimental +public class BatchRequestException extends RuntimeException { + + private final List> results; + + /** + * Constructs a new BatchRequestException with the specified message and results. + * + * @param The type of the request + * @param The type of the response + * @param message The error message describing the batch operation failure + * @param allResults List of results from all attempted requests in the batch + */ + public , REP extends Result> BatchRequestException( + String message, List> allResults) { + super(message); + this.results = List.copyOf(allResults); + } + + /** + * Returns the list of results from all requests that were part of the batch operation. + * Each result contains the original request, the response (if successful), and any error + * that occurred during processing. + * + * @return An unmodifiable list of request results + */ + public List> getResults() { + return results; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/CacheMetadata.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/CacheMetadata.java new file mode 100644 index 000000000000..ecc9920b226d --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/CacheMetadata.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nullable; + +/** + * Interface defining metadata for cache behavior and lifecycle management. + * Implementations can specify how long cached data should be retained. + * + * @since 4.0.0 + */ +@Experimental +public interface CacheMetadata { + + /** + * Returns the cache retention that should be applied to the associated data. + * + * @return The CacheRetention indicating how long data should be retained, or null if + * no specific cache retention is defined + */ + @Nullable + CacheRetention getCacheRetention(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/CacheRetention.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/CacheRetention.java new file mode 100644 index 000000000000..4ec20db8422e --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/CacheRetention.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Enumeration defining different retention periods for cached data. + * Each value represents a specific scope and lifetime for cached items. + * + * @since 4.0.0 + */ +@Experimental +public enum CacheRetention { + /** + * Data should be persisted across Maven invocations. + * Suitable for: + * - Dependency resolution results + * - Compilation outputs + * - Downloaded artifacts + */ + PERSISTENT, + + /** + * Data should be retained for the duration of the current Maven session. + * Suitable for: + * - Build-wide configuration + * - Project model caching + * - Inter-module metadata + */ + SESSION_SCOPED, + + /** + * Data should only be retained for the current build request. + * Suitable for: + * - Plugin execution results + * - Temporary build artifacts + * - Phase-specific data + */ + REQUEST_SCOPED, + + /** + * Caching should be disabled for this data. + * Suitable for: + * - Sensitive information + * - Non-deterministic operations + * - Debug or development data + */ + DISABLED +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/MavenExecutionException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/MavenExecutionException.java new file mode 100644 index 000000000000..a8862e2d339e --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/MavenExecutionException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.MavenException; + +/** + * Exception thrown when an error occurs during Maven execution. + * This exception wraps the original cause of the execution failure. + * + * @since 4.0.0 + */ +@Experimental +public class MavenExecutionException extends MavenException { + + /** + * Constructs a new MavenExecutionException with the specified cause. + * + * @param cause The underlying exception that caused the execution failure + */ + public MavenExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestCache.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestCache.java new file mode 100644 index 000000000000..2a4ad69d834d --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestCache.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import java.util.List; +import java.util.function.Function; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.Request; +import org.apache.maven.api.services.Result; + +/** + * Interface for caching request results in Maven. This cache implementation provides + * methods for executing and optionally caching both single requests and batches of requests. + *

+ * The cache behavior is determined by the cache retention specified in the request's metadata. + * Results can be cached at different policies (forever, session, request, or not at all) + * based on the {@link CacheRetention} associated with the request. + * + * @since 4.0.0 + * @see CacheMetadata + * @see RequestCacheFactory + */ +@Experimental +public interface RequestCache { + + /** + * Executes and optionally caches a request using the provided supplier function. If caching is enabled + * for this session, the result will be cached and subsequent identical requests will return the cached + * value without re-executing the supplier. + *

+ * The caching behavior is determined by the cache retention specified in the request's metadata. + * If an error occurs during execution, it will be cached and re-thrown for subsequent identical requests. + * + * @param The request type + * @param The response type + * @param req The request object used as the cache key + * @param supplier The function to execute and cache the result + * @return The result from the supplier (either fresh or cached) + * @throws RuntimeException Any exception thrown by the supplier will be cached and re-thrown on subsequent calls + */ + , REP extends Result> REP request(REQ req, Function supplier); + + /** + * Executes and optionally caches a batch of requests using the provided supplier function. + * This method allows for efficient batch processing of multiple requests. + *

+ * The implementation may optimize the execution by: + *

    + *
  • Returning cached results for previously executed requests
  • + *
  • Grouping similar requests for batch processing
  • + *
  • Processing requests in parallel where appropriate
  • + *
+ * + * @param The request type + * @param The response type + * @param req List of requests to process + * @param supplier Function to execute the batch of requests + * @return List of results corresponding to the input requests + * @throws BatchRequestException if any request in the batch fails + */ + , REP extends Result> List requests( + List req, Function, List> supplier); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestCacheFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestCacheFactory.java new file mode 100644 index 000000000000..dff86d521f76 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestCacheFactory.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Factory interface for creating new RequestCache instances. + * Implementations should handle the creation and configuration of cache instances + * based on the current Maven session and environment. + * + * @since 4.0.0 + * @see RequestCache + */ +@Experimental +public interface RequestCacheFactory { + + /** + * Creates a new RequestCache instance. + * The created cache should be configured according to the current Maven session + * and environment settings. + * + * @return A new RequestCache instance + */ + RequestCache createCache(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestResult.java new file mode 100644 index 000000000000..2876a3bb6a97 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/RequestResult.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.cache; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.Request; +import org.apache.maven.api.services.Result; + +/** + * A record representing the result of a single request operation, containing the original request, + * the result (if successful), and any error that occurred during processing. + *

+ * This class is immutable and thread-safe, suitable for use in concurrent operations. + * + * @param The type of the request + * @param The type of the response, which must extend {@code Result} + * @param request The original request that was processed + * @param result The result of the request, if successful; may be null if an error occurred + * @param error Any error that occurred during processing; null if the request was successful + * @since 4.0.0 + */ +@Experimental +public record RequestResult, REP extends Result>( + /** + * The original request that was processed + */ + REQ request, + + /** + * The result of the request, if successful; may be null if an error occurred + */ + REP result, + + /** + * Any error that occurred during processing; null if the request was successful + */ + Throwable error) { + + /** + * Determines if the request was processed successfully. + * + * @return true if no error occurred during processing (error is null), false otherwise + */ + public boolean isSuccess() { + return error == null; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/cache/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/package-info.java new file mode 100644 index 000000000000..9e6a0f458542 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/cache/package-info.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides a caching infrastructure for Maven requests and their results. + *

+ * This package contains the core components for implementing and managing caches in Maven: + *

    + *
  • {@link org.apache.maven.api.cache.RequestCache} - The main interface for caching request results
  • + *
  • {@link org.apache.maven.api.cache.RequestCacheFactory} - Factory for creating cache instances
  • + *
  • {@link org.apache.maven.api.cache.CacheMetadata} - Configuration for cache behavior and lifecycle
  • + *
+ *

+ * The caching system supports different retention periods through {@link org.apache.maven.api.cache.CacheRetention}: + *

    + *
  • PERSISTENT - Data persists across Maven invocations
  • + *
  • SESSION_SCOPED - Data retained for the duration of a Maven session
  • + *
  • REQUEST_SCOPED - Data retained only for the current build request
  • + *
  • DISABLED - No caching performed
  • + *
+ *

+ * Example usage: + *

+ * RequestCache cache = cacheFactory.createCache();
+ * Result result = cache.request(myRequest, req -> {
+ *     // Expensive operation to compute result
+ *     return computedResult;
+ * });
+ * 
+ *

+ * The package also provides support for batch operations through {@link org.apache.maven.api.cache.BatchRequestException} + * and {@link org.apache.maven.api.cache.RequestResult} which help manage multiple requests and their results. + * + * @since 4.0.0 + */ +@Experimental +package org.apache.maven.api.cache; + +import org.apache.maven.api.annotations.Experimental; diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java new file mode 100644 index 000000000000..0005433d63d1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.feature; + +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.api.Constants; +import org.apache.maven.api.annotations.Nullable; + +/** + * Centralized class for Maven Core feature information. + * Features configured are supposed to be final in a given maven session. + * + * @since 4.0.0 + */ +public final class Features { + + private Features() {} + + /** + * Check if the personality is "maven3". + */ + public static boolean mavenMaven3Personality(@Nullable Map userProperties) { + return doGet(userProperties, Constants.MAVEN_MAVEN3_PERSONALITY, false); + } + + /** + * Check if the consumer POM feature is active. + */ + public static boolean consumerPom(@Nullable Map userProperties) { + return doGet(userProperties, Constants.MAVEN_CONSUMER_POM, !mavenMaven3Personality(userProperties)); + } + + /** + * Check if build POM deployment is enabled. + */ + public static boolean deployBuildPom(@Nullable Map userProperties) { + return doGet(userProperties, Constants.MAVEN_DEPLOY_BUILD_POM, true); + } + + private static boolean doGet(Properties userProperties, String key, boolean def) { + return doGet(userProperties != null ? userProperties.get(key) : null, def); + } + + private static boolean doGet(Map userProperties, String key, boolean def) { + return doGet(userProperties != null ? userProperties.get(key) : null, def); + } + + private static boolean doGet(Object val, boolean def) { + if (val instanceof Boolean bool) { + return bool; + } else if (val != null) { + return Boolean.parseBoolean(val.toString()); + } else { + return def; + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/feature/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/package-info.java new file mode 100644 index 000000000000..b10b38ad3cc3 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/package-info.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides feature flag management and configuration capabilities for Maven core functionality. + * + * @since 4.0.0 + */ +package org.apache.maven.api.feature; diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/package-info.java new file mode 100644 index 000000000000..7fd2d60b9591 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/package-info.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + *

Maven Core API

+ * + *

Session

+ * + *

The {@link org.apache.maven.api.Session} interface is the main entry point for Maven operations. + * It maintains the state of a Maven execution and provides access to all core services and components. + * Sessions are thread-safe and can be obtained in session-scoped components using the + * {@link org.apache.maven.api.di.SessionScoped} annotation.

+ * + *

Key capabilities provided through the Session include:

+ *
    + *
  • Access to the current {@link org.apache.maven.api.Project}
  • + *
  • Access to the {@link org.apache.maven.api.LocalRepository} and {@link org.apache.maven.api.RemoteRepository} configurations
  • + *
  • Access to Maven services through {@link org.apache.maven.api.Session#getService(Class)}
  • + *
  • Build configuration and settings
  • + *
+ * + *

Dependency management

+ * + *

{@link org.apache.maven.api.ArtifactCoordinates} instances are used to locate artifacts in a repository. + * Each instance identifies an artifact or version range of artifacts in the Maven repository system.

+ * + *

{@link org.apache.maven.api.Artifact} instances represent artifacts in the repository. + * They are created when resolving an {@code ArtifactCoordinates} object. Resolving is the process + * that selects a particular version and downloads the artifact into the local repository. + * The {@link org.apache.maven.api.DownloadedArtifact} sub-interface is used when + * an artifact has been resolved and the {@link org.apache.maven.api.ProducedArtifact} sub-interface when + * an artifact is being produced by a project.

+ * + *

{@link org.apache.maven.api.DependencyCoordinates} instances represent a dependency element in a POM. + * A {@code DependencyCoordinates} extends {@code ArtifactCoordinates} with additional information about how + * the artifact will be used: type, scope and obligation (whether the dependency is optional or mandatory). + * The version and the obligation may not be defined precisely.

+ * + *

{@link org.apache.maven.api.Dependency} instances represent artifacts in the repository + * that are dependencies of the project. + * They are created when resolving a {@code DependencyCoordinates}. + * Resolving is the process that clarifies the obligation (optional or mandatory), + * selects a particular version, and downloads the artifact into the local repository.

+ * + *

{@link org.apache.maven.api.Node} is the main output of the dependency collection process. + * It's a node in the dependency graph and contains a {@code Dependency} instance. The {@code Dependency} instances are the outputs of the + * collection process, part of the graph computed from one or more {@code DependencyCoordinates}.

+ * + *

{@link org.apache.maven.api.DependencyScope} defines when/how a given dependency will be used by the + * project. This includes compile-time only, runtime, test time, and various other combinations.

+ * + *

Resolution

+ * + *

Version resolution is the process of finding, for a given artifact, a list of + * versions that match the input {@linkplain org.apache.maven.api.VersionConstraint version constraint} + * in the list of remote repositories. This is done either explicitly using the + * {@link org.apache.maven.api.services.VersionResolver VersionResolver} service, or implicitly when resolving + * an artifact.

+ * + *

Artifact resolution is the process of {@linkplain org.apache.maven.api.services.VersionResolver + * resolving the version} and then downloading the file.

+ * + *

Dependency collection builds a graph of {@link org.apache.maven.api.Node} objects containing + * all the dependencies.

+ * + *

The Dependency graph flattening process in Maven reduces a complex, + * multi-level dependency graph to a map of ordered lists that can be turned into classpaths. + * During this process only the most relevant version of each artifact + * (based on group ID and artifact ID) is retained, resolving conflicts and eliminating duplicates to ensure + * that each dependency is included only once in the final build.

+ * + *

Dependency resolution is the process of collecting dependencies, flattening the result graph, + * and then resolving the artifacts.

+ * + *

Repositories

+ * + *

In Maven, {@linkplain org.apache.maven.api.Repository repositories} are locations where project artifacts (such as JAR files, POM files, and other + * resources) are stored and retrieved. There are two primary types of repositories:

    + *
  • {@linkplain org.apache.maven.api.LocalRepository local repository}: A directory on the developer's machine where Maven caches + * downloaded artifacts.
  • + *
  • {@linkplain org.apache.maven.api.RemoteRepository remote repository}: A central or distributed location from which Maven can download artifacts + * when they are not available locally.
  • + *
+ * + *

When resolving artifacts, Maven follows this order:

    + *
  1. Check Local Repository: Maven first checks if the artifact is available in the local repository.
  2. + *
  3. Check Remote Repositories: If the artifact is not found locally, Maven queries the configured remote repositories in the order they are listed.
  4. + *
  5. Download and Cache: If Maven finds the artifact in a remote repository, it downloads it and stores it in the local repository for future use.
  6. + *
+ *

By caching artifacts in the local repository, Maven minimizes the need to repeatedly download the same artifacts, thus optimizing the build process.

+ * + *

Projects

+ * + *

{@link org.apache.maven.api.Project} instances are loaded by Maven from the local + * file system (those projects are usually about to be built) or from the local repository + * (they are usually downloaded during dependency collection). Those projects are loaded + * from a Project Object Model (POM).

+ * + *

Project Object Model or POM refers to the information describing + * all the information needed to build or consume a project. Those are usually loaded from + * a file named {@code pom.xml} and loaded into a {@link org.apache.maven.api.model.Model Model} + * instances.

+ * + *

Project aggregation allows building several projects together. This is only + * for projects that are built, hence available on the file system. One project, + * called the aggregator project lists one or more modules + * which are relative pointers on the file system to other projects. This is done using + * the {@code /project/modules/module} elements of the POM in the aggregator project. + * Note that the aggregator project is required to have a {@code pom} packaging.

+ * + *

Project inheritance defines a parent-child relationship between projects. + * The child project inherits all the information from the parent project + * POM.

+ * + */ +package org.apache.maven.api; diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/LifecycleProvider.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/LifecycleProvider.java new file mode 100644 index 000000000000..1f0d61b2c413 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/LifecycleProvider.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin; + +import java.util.List; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.plugin.descriptor.lifecycle.Lifecycle; + +/** + * Interface that can be provided by the plugin to wire in custom lifecycles + * leveraged using the {@link org.apache.maven.api.plugin.annotations.Execute} + * annotation. If a {@code META-INF/maven/lifecycle.xml} file is packaged + * in the plugin, Maven will provide a default implementation that will parse + * the file and return the contained lifecycle definitions. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface LifecycleProvider { + + List getLifecycles(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java new file mode 100644 index 000000000000..f2968295e456 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin; + +import java.util.function.Supplier; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Provider; + +/** + * This interface supplies the API for providing feedback to the user from the {@code Mojo}, + * using standard Maven channels. + * There should be no big surprises here, although you may notice that the methods accept + * java.lang.CharSequence rather than java.lang.String. This is provided mainly as a + * convenience, to enable developers to pass things like java.lang.StringBuffer directly into the logger, + * rather than formatting first by calling toString(). + * + * @since 4.0.0 + */ +@Experimental +@Provider +public interface Log { + /** + * {@return true if the debug error level is enabled} + */ + boolean isDebugEnabled(); + + /** + * Sends a message to the user in the debug error level. + * + * @param content the message to log + */ + void debug(CharSequence content); + + /** + * Sends a message (and accompanying exception) to the user in the debug error level. + * The error's stacktrace will be output when this error level is enabled. + * + * @param content the message to log + * @param error the error that caused this log + */ + void debug(CharSequence content, Throwable error); + + /** + * Sends an exception to the user in the debug error level. + * The stack trace for this exception will be output when this error level is enabled. + * + * @param error the error that caused this log + */ + void debug(Throwable error); + + void debug(Supplier content); + + void debug(Supplier content, Throwable error); + + /** + * {@return true if the info error level is enabled} + */ + boolean isInfoEnabled(); + + /** + * Sends a message to the user in the info error level. + * + * @param content the message to log + */ + void info(CharSequence content); + + /** + * Sends a message (and accompanying exception) to the user in the info error level. + * The error's stacktrace will be output when this error level is enabled. + * + * @param content the message to log + * @param error the error that caused this log + */ + void info(CharSequence content, Throwable error); + + /** + * Sends an exception to the user in the info error level. + * The stack trace for this exception will be output when this error level is enabled. + * + * @param error the error that caused this log + */ + void info(Throwable error); + + void info(Supplier content); + + void info(Supplier content, Throwable error); + + /** + * {@return true if the warn error level is enabled} + */ + boolean isWarnEnabled(); + + /** + * Sends a message to the user in the warn error level. + * + * @param content the message to log + */ + void warn(CharSequence content); + + /** + * Sends a message (and accompanying exception) to the user in the warn error level. + * The error's stacktrace will be output when this error level is enabled. + * + * @param content the message to log + * @param error the error that caused this log + */ + void warn(CharSequence content, Throwable error); + + /** + * Sends an exception to the user in the warn error level. + * The stack trace for this exception will be output when this error level is enabled. + * + * @param error the error that caused this log + */ + void warn(Throwable error); + + void warn(Supplier content); + + void warn(Supplier content, Throwable error); + + /** + * {@return true if the error error level is enabled} + */ + boolean isErrorEnabled(); + + /** + * Sends a message to the user in the error error level. + * + * @param content the message to log + */ + void error(CharSequence content); + + /** + * Sends a message (and accompanying exception) to the user in the error error level. + * The error's stacktrace will be output when this error level is enabled. + * + * @param content the message to log + * @param error the error that caused this log + */ + void error(CharSequence content, Throwable error); + + /** + * Sends an exception to the user in the error error level. + * The stack trace for this exception will be output when this error level is enabled. + * + * @param error the error that caused this log + */ + void error(Throwable error); + + void error(Supplier content); + + void error(Supplier content, Throwable error); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Mojo.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Mojo.java new file mode 100644 index 000000000000..277d4ba6ffe1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Mojo.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.ThreadSafe; + +/** + * Represents the contract for Mojos to interact with the Maven infrastructure. + * Implementations of this interface define specific build-process behaviors + * that are triggered during a Maven build lifecycle. + * + * The primary entry point is the {@link #execute()} method, which encapsulates + * the behavior of the Mojo and serves as the integration point with Maven. This + * method may throw an {@link Exception} to signal any issues that prevent + * successful execution of the Mojo. + * + *

+ * Annotations: + *

+ *
    + *
  • {@link Experimental}: Indicates that this interface or its implementation + * may still be evolving and could change in future versions.
  • + *
  • {@link FunctionalInterface}: Denotes that this is a functional interface, + * allowing implementations as lambda expressions or method references.
  • + *
  • {@link Consumer}: Signifies that this type is intended to be implemented + * or extended by Maven plugins or extensions and consumed by Maven itself.
  • + *
  • {@link ThreadSafe}: Implies that implementations of this interface must + * be safe to invoke from multiple threads concurrently.
  • + *
+ * + * @since 4.0.0 + */ +@Experimental +@FunctionalInterface +@Consumer +@ThreadSafe +public interface Mojo { + /** + * Executes the behavior defined by this {@code Mojo}. This method is invoked + * during the Maven build lifecycle to perform the Mojo's designated task. + * + *

Implementations should handle any task-specific logic and may communicate + * errors by throwing an {@link Exception}. Error conditions should provide + * sufficient detail to aid troubleshooting.

+ * + * @throws Exception if any issue occurs that prevents the successful execution + * of the Mojo + */ + void execute() throws Exception; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/MojoException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/MojoException.java new file mode 100644 index 000000000000..2d4b976cee79 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/MojoException.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.MavenException; + +/** + * An exception occurring during the execution of a plugin. + * + * @since 4.0.0 + */ +@Experimental +public class MojoException extends MavenException { + + protected Object source; + + protected String longMessage; + + /** + * Constructs a new {@code MojoException} providing the source and a short and long message. + * These messages are used to improve the message written at the end of Maven build. + */ + public MojoException(Object source, String shortMessage, String longMessage) { + super(shortMessage); + this.source = source; + this.longMessage = longMessage; + } + + /** + * Constructs a new {@code MojoException} wrapping an underlying {@code Throwable} + * and providing a {@code message}. + */ + public MojoException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new {@code MojoException} providing a {@code message}. + */ + public MojoException(String message) { + super(message); + } + + /** + * Constructs a new {@code MojoExecutionException} wrapping an underlying {@code Throwable}. + * + * @param cause the cause which is saved for later retrieval by the {@link #getCause()} method. + * A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown. + */ + public MojoException(Throwable cause) { + super(cause); + } + + public String getLongMessage() { + return longMessage; + } + + public Object getSource() { + return source; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java new file mode 100644 index 000000000000..697b9d480206 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Specifies that the mojo should be run after the specific phase. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface After { + + /** + * Type of pointer. + * @see org.apache.maven.api.Lifecycle.Pointer.Type + */ + enum Type { + PROJECT, + DEPENDENCIES, + CHILDREN + } + + /** + * The phase name. + */ + String phase(); + + /** + * The type of this pointer. + */ + Type type(); + + /** + * The scope for dependencies, only if {@code type() == Type.Dependencies}. + */ + String scope() default ""; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java new file mode 100644 index 000000000000..f4830b87ae0b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Used if your Mojo needs to fork a lifecycle. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface Execute { + /** + * Lifecycle phase to fork. Note that specifying a phase overrides specifying a goal. + * @return the phase + */ + @Nonnull + String phase() default ""; + + /** + * Goal to fork. Note that specifying a phase overrides specifying a goal. The specified goal must be + * another goal of the same plugin. + * @return the goal + */ + @Nonnull + String goal() default ""; + + /** + * Lifecycle id of the lifecycle that defines {@link #phase()}. Only valid in combination with {@link #phase()}. If + * not specified, Maven will use the lifecycle of the current build. + * + * @see Lifecycle Mappings + * @return the lifecycle id + */ + @Nonnull + String lifecycle() default ""; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java new file mode 100644 index 000000000000..009c2541f369 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * This annotation will mark your class as a Mojo, which is the implementation of a goal in a Maven plugin. + *

+ * The mojo can be annotated with {@code org.apache.maven.api.di.*} annotations to + * control the lifecycle of the mojo itself, and to inject other beans. + *

+ *

+ * The mojo class can also be injected with an {@link Execute} annotation to specify a + * forked lifecycle. + *

+ *

+ * The {@link Parameter} annotation can be added on fields to inject data + * from the plugin configuration or from other components. + *

+ *

+ * Fields can also be annotated with the {@link Resolution} annotation to be injected + * with the dependency collection or resolution result for the project. + *

+ * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface Mojo { + /** + * goal name (required). + * @return the goal name + */ + @Nonnull + String name(); + + /** + * default phase to bind your mojo. + * @return the default phase + */ + @Nonnull + String defaultPhase() default ""; + + /** + * does your mojo requires a project to be executed? + * @return requires a project + */ + boolean projectRequired() default true; + + /** + * if the Mojo uses the Maven project and its subprojects. + * @return uses the Maven project and its subprojects + */ + boolean aggregator() default false; + + /** + * does this Mojo need to be online to be executed? + * @return need to be online + */ + boolean onlineRequired() default false; + + /** + * TODO: v4: add a SPI for the configurator + * configurator bean name. + * @return the configurator bean name + */ + @Nonnull + String configurator() default ""; + + /** + * Indicates whether dependency collection will be + * required when executing the Mojo. + * If not set, it will be inferred from the fields + * annotated with the {@link Resolution} annotation. + */ + @Nonnull + boolean dependencyCollection() default false; + + /** + * Comma separated list of path scopes that will be + * required for dependency resolution. + * If not set, it will be inferred from the fields + * annotated with the {@link Resolution} annotation. + */ + @Nonnull + String dependencyResolutionPathScopes() default ""; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java new file mode 100644 index 000000000000..64b129ae4ebe --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Used to configure your Mojo parameters to be injected by + * + * MavenPluginManager.getConfiguredMojo(...). + *

+ * Beans injected into Mojo parameters are prepared by Sisu JSR330-based + * container: this annotation is only effective on fields of the Mojo class itself, nested bean injection + * requires Sisu or JSR330 annotations. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Inherited +public @interface Parameter { + /** + * name of the bean property used to get/set the field: by default, field name is used. + * @return the name of the bean property + */ + @Nonnull + String name() default ""; + + /** + * alias supported to get parameter value. + * @return the alias + */ + @Nonnull + String alias() default ""; + + /** + * Property to use to retrieve a value. Can come from -D execution, setting properties or pom + * properties. + * @return property name + */ + @Nonnull + String property() default ""; + + /** + * parameter default value, may contain ${...} expressions which will be interpreted at + * inject time: see + * + * PluginParameterExpressionEvaluator. + * @return the default value + */ + @Nonnull + String defaultValue() default ""; + + /** + * is the parameter required? + * @return true if the Mojo should fail when the parameter cannot be injected + */ + boolean required() default false; + + /** + * Specifies that this parameter cannot be configured directly by the user (as in the case of POM-specified + * configuration). This is useful when you want to force the user to use common POM elements rather than plugin + * configurations, as in the case where you want to use the artifact's final name as a parameter. In this case, you + * want the user to modify <build><finalName/></build> rather than specifying a value + * for finalName directly in the plugin configuration section. It is also useful to ensure that - for example - a + * List-typed parameter which expects items of type Artifact doesn't get a List full of Strings. + * + * @return true if the user should not be allowed to configure the parameter directly + */ + boolean readonly() default false; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Resolution.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Resolution.java new file mode 100644 index 000000000000..6e745a0dd417 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Resolution.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Indicates that a given field will be injected with the result of + * a dependency collection or resolution request. Whether a collection + * or resolution request is performed is controlled by the {@link #pathScope()} + * field, the injected field type and the {@link #requestType()}. + *

+ * If the {@code requestType} is not set explicitly, it will be inferred + * from the {@code pathScope} and the injected field type. If the type + * is {@link org.apache.maven.api.Node Node} and {@code pathScope == ""}, + * then the dependencies will be collected. + * If the type is {@link org.apache.maven.api.Node Node} or + * {@code List<}{@link org.apache.maven.api.Node Node}{@code >}, + * and {@code pathScope != ""}, the dependencies will be flattened. + * Else the dependencies will be resolved and {@code pathScope} must be non empty, + * and the field type can be {@link org.apache.maven.api.Node Node}, + * {@code List<}{@link org.apache.maven.api.Node Node}{@code >}, + * {@link org.apache.maven.api.services.DependencyResolverResult DependencyResolverResult}, + * {@code List<}{@link java.nio.file.Path Path}{@code >}, + * {@code Map<}{@link org.apache.maven.api.PathType PathType}{@code , List<}{@link java.nio.file.Path Path}{@code >>}, + * or {@code Map<}{@link org.apache.maven.api.Dependency Dependency}{@code , }{@link java.nio.file.Path Path}{@code >}. + * + * @since 4.0.0 + */ +@Experimental +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Resolution { + + /** + * The id of a {@link org.apache.maven.api.PathScope} enum value. + * If specified, a dependency resolution request will be issued, + * else a dependency collection request will be done. + * + * @return the id of the path scope + */ + String pathScope() default ""; + + /** + * The request type, in case the default one is not correct. + * Valid values are {@code collect}, {@code flatten}, or {@code resolve}. + * + * @return the request type + */ + String requestType() default ""; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java new file mode 100644 index 000000000000..14d2c7a2ef1c --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Maven Plugin Annotations. + */ +package org.apache.maven.api.plugin.annotations; diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/package-info.java new file mode 100644 index 000000000000..1bed56a81bfe --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Maven Plugin API. + */ +package org.apache.maven.api.plugin; diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactory.java new file mode 100644 index 000000000000..50fa35c04c4e --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactory.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Service used to create {@link ArtifactCoordinates} objects. + * + * @since 4.0.0 + */ +@Experimental +public interface ArtifactCoordinatesFactory extends Service { + + /** + * Creates artifact coordinates. + * + * @param request the request holding coordinates creation parameters + * @return an {@code ArtifactCoordinates}, never {@code null} + * @throws IllegalArgumentException if {@code request} is null or {@code request.session} is null or invalid + */ + @Nonnull + ArtifactCoordinates create(@Nonnull ArtifactCoordinatesFactoryRequest request); + + /** + * Creates coordinates out of string that is formatted like: + * {@code :[:[:]]:} + * + * @param session the session. + * @param coordinatesString the string having "standard" coordinates. + * @return an {@code ArtifactCoordinates}, never {@code null} + * @throws IllegalArgumentException if {@code session} is null or invalid + */ + @Nonnull + default ArtifactCoordinates create(@Nonnull Session session, @Nonnull String coordinatesString) { + return create(ArtifactCoordinatesFactoryRequest.build(session, coordinatesString)); + } + + @Nonnull + default ArtifactCoordinates create( + @Nonnull Session session, String groupId, String artifactId, String version, String extension) { + return create(ArtifactCoordinatesFactoryRequest.build(session, groupId, artifactId, version, extension)); + } + + @Nonnull + default ArtifactCoordinates create( + @Nonnull Session session, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + return create(ArtifactCoordinatesFactoryRequest.build( + session, groupId, artifactId, version, classifier, extension, type)); + } + + @Nonnull + default ArtifactCoordinates create(@Nonnull Session session, Artifact artifact) { + return create(ArtifactCoordinatesFactoryRequest.build( + session, + artifact.getGroupId(), + artifact.getArtifactId(), + artifact.getVersion().toString(), + artifact.getClassifier(), + artifact.getExtension(), + null)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java new file mode 100644 index 000000000000..d2c3540f3c2b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Objects; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; + +import static java.util.Objects.requireNonNull; + +/** + * A request for creating a {@link ArtifactCoordinates} object. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ArtifactCoordinatesFactoryRequest extends Request { + + String getGroupId(); + + String getArtifactId(); + + String getVersion(); + + String getClassifier(); + + String getExtension(); + + String getType(); + + String getCoordinatesString(); + + @Nonnull + static ArtifactCoordinatesFactoryRequest build( + @Nonnull Session session, String groupId, String artifactId, String version, String extension) { + return ArtifactCoordinatesFactoryRequest.builder() + .session(requireNonNull(session, "session")) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .extension(extension) + .build(); + } + + @Nonnull + static ArtifactCoordinatesFactoryRequest build( + @Nonnull Session session, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + return ArtifactCoordinatesFactoryRequest.builder() + .session(requireNonNull(session, "session")) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .classifier(classifier) + .extension(extension) + .type(type) + .build(); + } + + @Nonnull + static ArtifactCoordinatesFactoryRequest build(@Nonnull Session session, @Nonnull String coordinateString) { + return ArtifactCoordinatesFactoryRequest.builder() + .session(requireNonNull(session, "session")) + .coordinateString(requireNonNull(coordinateString, "coordinateString")) + .build(); + } + + @Nonnull + static ArtifactCoordinatesFactoryRequest build(@Nonnull Session session, @Nonnull ArtifactCoordinates coordinates) { + return ArtifactCoordinatesFactoryRequest.builder() + .session(requireNonNull(session, "session")) + .groupId(requireNonNull(coordinates, "coordinates").getGroupId()) + .artifactId(coordinates.getArtifactId()) + .classifier(coordinates.getClassifier()) + .version(coordinates.getVersionConstraint().toString()) + .extension(coordinates.getExtension()) + .build(); + } + + static ArtifactFactoryRequestBuilder builder() { + return new ArtifactFactoryRequestBuilder(); + } + + @NotThreadSafe + class ArtifactFactoryRequestBuilder { + private Session session; + private RequestTrace trace; + private String groupId; + private String artifactId; + private String version; + private String classifier; + private String extension; + private String type; + private String coordinateString; + + ArtifactFactoryRequestBuilder() {} + + public ArtifactFactoryRequestBuilder session(Session session) { + this.session = session; + return this; + } + + public ArtifactFactoryRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public ArtifactFactoryRequestBuilder groupId(String groupId) { + this.groupId = groupId; + return this; + } + + public ArtifactFactoryRequestBuilder artifactId(String artifactId) { + this.artifactId = artifactId; + return this; + } + + public ArtifactFactoryRequestBuilder version(String version) { + this.version = version; + return this; + } + + public ArtifactFactoryRequestBuilder classifier(String classifier) { + this.classifier = classifier; + return this; + } + + public ArtifactFactoryRequestBuilder extension(String extension) { + this.extension = extension; + return this; + } + + public ArtifactFactoryRequestBuilder type(String type) { + this.type = type; + return this; + } + + public ArtifactFactoryRequestBuilder coordinateString(String coordinateString) { + this.coordinateString = coordinateString; + return this; + } + + public ArtifactCoordinatesFactoryRequest build() { + return new DefaultArtifactFactoryRequestArtifact( + session, trace, groupId, artifactId, version, classifier, extension, type, coordinateString); + } + + private static class DefaultArtifactFactoryRequestArtifact extends BaseRequest + implements ArtifactCoordinatesFactoryRequest { + private final String groupId; + private final String artifactId; + private final String version; + private final String classifier; + private final String extension; + private final String type; + private final String coordinatesString; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultArtifactFactoryRequestArtifact( + @Nonnull Session session, + RequestTrace trace, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type, + String coordinatesString) { + super(session, trace); + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classifier = classifier; + this.extension = extension; + this.type = type; + this.coordinatesString = coordinatesString; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public String getArtifactId() { + return artifactId; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getClassifier() { + return classifier; + } + + @Override + public String getExtension() { + return extension; + } + + @Override + public String getType() { + return type; + } + + @Override + public String getCoordinatesString() { + return coordinatesString; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultArtifactFactoryRequestArtifact that + && Objects.equals(groupId, that.groupId) + && Objects.equals(artifactId, that.artifactId) + && Objects.equals(version, that.version) + && Objects.equals(classifier, that.classifier) + && Objects.equals(extension, that.extension) + && Objects.equals(type, that.type) + && Objects.equals(coordinatesString, that.coordinatesString); + } + + @Override + public int hashCode() { + return Objects.hash(groupId, artifactId, version, classifier, extension, type, coordinatesString); + } + + @Override + public String toString() { + return "ArtifactFactoryRequestArtifact[" + "groupId='" + + groupId + '\'' + ", artifactId='" + + artifactId + '\'' + ", version='" + + version + '\'' + ", classifier='" + + classifier + '\'' + ", extension='" + + extension + '\'' + ", type='" + + type + '\'' + ", coordinatesString='" + + coordinatesString + '\'' + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployer.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployer.java new file mode 100644 index 000000000000..d041960d577f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployer.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; + +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Deploys {@link ProducedArtifact}s to a {@link RemoteRepository}. + * + * @since 4.0.0 + * @see Session#deployArtifact(RemoteRepository, ProducedArtifact...) + */ +@Experimental +public interface ArtifactDeployer extends Service { + + /** + * @param request {@link ArtifactDeployerRequest} + * @throws ArtifactDeployerException if the deployment failed + */ + void deploy(@Nonnull ArtifactDeployerRequest request); + + /** + * @param session the repository session + * @param repository the repository to deploy to + * @param artifacts the collection of artifacts to deploy + * @throws ArtifactDeployerException if the deployment failed + * @throws IllegalArgumentException if an argument is {@code null} or invalid + */ + default void deploy( + @Nonnull Session session, + @Nonnull RemoteRepository repository, + @Nonnull Collection artifacts) { + deploy(ArtifactDeployerRequest.build(session, repository, artifacts)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java new file mode 100644 index 000000000000..6604c0392a11 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * An artifact could not correctly being deployed. + * + * @since 4.0.0 + */ +@Experimental +public class ArtifactDeployerException extends MavenException { + + /** + * + */ + @Serial + private static final long serialVersionUID = 7421964724059077698L; + + /** + * @param message the message of the error + * @param e {@link Exception} + */ + public ArtifactDeployerException(String message, Exception e) { + super(message, e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerRequest.java new file mode 100644 index 000000000000..8d54d882c0fb --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerRequest.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * A request for deploying one or more artifacts to a remote repository. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ArtifactDeployerRequest extends Request { + + @Nonnull + RemoteRepository getRepository(); + + @Nonnull + Collection getArtifacts(); + + int getRetryFailedDeploymentCount(); + + @Nonnull + static ArtifactDeployerRequestBuilder builder() { + return new ArtifactDeployerRequestBuilder(); + } + + @Nonnull + static ArtifactDeployerRequest build( + @Nonnull Session session, + @Nonnull RemoteRepository repository, + @Nonnull Collection artifacts) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .repository(requireNonNull(repository, "repository cannot be null")) + .artifacts(requireNonNull(artifacts, "artifacts cannot be null")) + .build(); + } + + class ArtifactDeployerRequestBuilder { + Session session; + RequestTrace trace; + RemoteRepository repository; + Collection artifacts; + int retryFailedDeploymentCount; + + ArtifactDeployerRequestBuilder() {} + + @Nonnull + public ArtifactDeployerRequestBuilder session(Session session) { + this.session = session; + return this; + } + + @Nonnull + public ArtifactDeployerRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + @Nonnull + public ArtifactDeployerRequestBuilder repository(RemoteRepository repository) { + this.repository = repository; + return this; + } + + public ArtifactDeployerRequestBuilder artifacts(Collection artifacts) { + this.artifacts = artifacts; + return this; + } + + public ArtifactDeployerRequestBuilder retryFailedDeploymentCount(int retryFailedDeploymentCount) { + this.retryFailedDeploymentCount = retryFailedDeploymentCount; + return this; + } + + @Nonnull + public ArtifactDeployerRequest build() { + return new DefaultArtifactDeployerRequest( + session, trace, repository, artifacts, retryFailedDeploymentCount); + } + + private static class DefaultArtifactDeployerRequest extends BaseRequest + implements ArtifactDeployerRequest { + + private final RemoteRepository repository; + private final Collection artifacts; + private final int retryFailedDeploymentCount; + + DefaultArtifactDeployerRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull RemoteRepository repository, + @Nonnull Collection artifacts, + int retryFailedDeploymentCount) { + super(session, trace); + this.repository = requireNonNull(repository, "repository cannot be null"); + this.artifacts = List.copyOf(requireNonNull(artifacts, "artifacts cannot be null")); + this.retryFailedDeploymentCount = retryFailedDeploymentCount; + } + + @Nonnull + @Override + public RemoteRepository getRepository() { + return repository; + } + + @Nonnull + @Override + public Collection getArtifacts() { + return artifacts; + } + + @Override + public int getRetryFailedDeploymentCount() { + return retryFailedDeploymentCount; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultArtifactDeployerRequest that + && retryFailedDeploymentCount == that.retryFailedDeploymentCount + && Objects.equals(repository, that.repository) + && Objects.equals(artifacts, that.artifacts); + } + + @Override + public int hashCode() { + return Objects.hash(repository, artifacts, retryFailedDeploymentCount); + } + + @Override + public String toString() { + return "ArtifactDeployerRequest[" + "repository=" + + repository + ", artifacts=" + + artifacts + ", retryFailedDeploymentCount=" + + retryFailedDeploymentCount + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactory.java new file mode 100644 index 000000000000..da77fc2d52bc --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactory.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Service used to create {@link Artifact} objects. + * + * @since 4.0.0 + */ +@Experimental +public interface ArtifactFactory extends Service { + + /** + * Creates an artifact. + * + * @param request the request holding artifact creation parameters + * @return an {@code Artifact}, never {@code null} + * @throws IllegalArgumentException if {@code request} is null or {@code request.session} is null or invalid + */ + @Nonnull + Artifact create(@Nonnull ArtifactFactoryRequest request); + + @Nonnull + default Artifact create( + @Nonnull Session session, String groupId, String artifactId, String version, String extension) { + return create(ArtifactFactoryRequest.build(session, groupId, artifactId, version, extension)); + } + + @Nonnull + default Artifact create( + @Nonnull Session session, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + return create(ArtifactFactoryRequest.build(session, groupId, artifactId, version, classifier, extension, type)); + } + + /** + * Creates an artifact. + * + * @param request the request holding artifact creation parameters + * @return an {@code Artifact}, never {@code null} + * @throws IllegalArgumentException if {@code request} is null or {@code request.session} is null or invalid + */ + @Nonnull + ProducedArtifact createProduced(@Nonnull ArtifactFactoryRequest request); + + @Nonnull + default ProducedArtifact createProduced( + @Nonnull Session session, String groupId, String artifactId, String version, String extension) { + return createProduced(ArtifactFactoryRequest.build(session, groupId, artifactId, version, extension)); + } + + @Nonnull + default ProducedArtifact createProduced( + @Nonnull Session session, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + return createProduced( + ArtifactFactoryRequest.build(session, groupId, artifactId, version, classifier, extension, type)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java new file mode 100644 index 000000000000..0d9792237400 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Objects; + +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ArtifactFactoryRequest extends Request { + + String getGroupId(); + + String getArtifactId(); + + String getVersion(); + + String getClassifier(); + + String getExtension(); + + String getType(); + + static ArtifactFactoryRequest build( + Session session, String groupId, String artifactId, String version, String extension) { + return ArtifactFactoryRequest.builder() + .session(requireNonNull(session, "session cannot be null")) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .extension(extension) + .build(); + } + + static ArtifactFactoryRequest build( + Session session, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + return ArtifactFactoryRequest.builder() + .session(requireNonNull(session, "session cannot be null")) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .classifier(classifier) + .extension(extension) + .type(type) + .build(); + } + + static ArtifactFactoryRequestBuilder builder() { + return new ArtifactFactoryRequestBuilder(); + } + + @NotThreadSafe + class ArtifactFactoryRequestBuilder { + private Session session; + private RequestTrace trace; + private String groupId; + private String artifactId; + private String version; + private String classifier; + private String extension; + private String type; + + ArtifactFactoryRequestBuilder() {} + + public ArtifactFactoryRequestBuilder session(Session session) { + this.session = session; + return this; + } + + public ArtifactFactoryRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public ArtifactFactoryRequestBuilder groupId(String groupId) { + this.groupId = groupId; + return this; + } + + public ArtifactFactoryRequestBuilder artifactId(String artifactId) { + this.artifactId = artifactId; + return this; + } + + public ArtifactFactoryRequestBuilder version(String version) { + this.version = version; + return this; + } + + public ArtifactFactoryRequestBuilder classifier(String classifier) { + this.classifier = classifier; + return this; + } + + public ArtifactFactoryRequestBuilder extension(String extension) { + this.extension = extension; + return this; + } + + public ArtifactFactoryRequestBuilder type(String type) { + this.type = type; + return this; + } + + public ArtifactFactoryRequest build() { + return new DefaultArtifactFactoryRequest( + session, trace, groupId, artifactId, version, classifier, extension, type); + } + + private static class DefaultArtifactFactoryRequest extends BaseRequest + implements ArtifactFactoryRequest { + private final String groupId; + private final String artifactId; + private final String version; + private final String classifier; + private final String extension; + private final String type; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultArtifactFactoryRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + super(session, trace); + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classifier = classifier; + this.extension = extension; + this.type = type; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public String getArtifactId() { + return artifactId; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getClassifier() { + return classifier; + } + + @Override + public String getExtension() { + return extension; + } + + @Override + public String getType() { + return type; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultArtifactFactoryRequest that + && Objects.equals(groupId, that.groupId) + && Objects.equals(artifactId, that.artifactId) + && Objects.equals(version, that.version) + && Objects.equals(classifier, that.classifier) + && Objects.equals(extension, that.extension) + && Objects.equals(type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(groupId, artifactId, version, classifier, extension, type); + } + + @Override + public String toString() { + return "ArtifactFactoryRequest[" + "groupId='" + + groupId + '\'' + ", artifactId='" + + artifactId + '\'' + ", version='" + + version + '\'' + ", classifier='" + + classifier + '\'' + ", extension='" + + extension + '\'' + ", type='" + + type + '\'' + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstaller.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstaller.java new file mode 100644 index 000000000000..f17daa76acee --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstaller.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Installs {@link ProducedArtifact}s to the local repository. + * + * @since 4.0.0 + * @see Session#withLocalRepository(org.apache.maven.api.LocalRepository) + */ +@Experimental +public interface ArtifactInstaller extends Service { + /** + * @param request {@link ArtifactInstallerRequest} + * @throws ArtifactInstallerException in case of an error + * @throws IllegalArgumentException in case {@code request} is {@code null} + */ + void install(@Nonnull ArtifactInstallerRequest request); + + /** + * @param session the repository session + * @param artifact the {@link ProducedArtifact} to install + * @throws ArtifactInstallerException in case of an error which can be a given artifact cannot be found or the + * installation has failed + * @throws IllegalArgumentException if a parameter {@code session} is {@code null} or + * {@code artifact} is {@code null} + */ + default void install(Session session, ProducedArtifact artifact) { + install(session, Collections.singletonList(artifact)); + } + + /** + * @param session the repository session + * @param artifacts Collection of {@link ProducedArtifact MavenArtifacts} + * @throws ArtifactInstallerException if the given artifact cannot be found or the + * installation has failed + * @throws IllegalArgumentException if {@code request} is {@code null} or parameter + * {@code localRepository} is {@code null} or {@code localRepository} is not a directory + * or parameter {@code mavenArtifacts} is {@code null} or + * {@code mavenArtifacts.isEmpty()} is {@code true}. + */ + default void install(Session session, Collection artifacts) { + install(ArtifactInstallerRequest.build(session, artifacts)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java new file mode 100644 index 000000000000..8405fc301766 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * @since 4.0.0 + */ +@Experimental +public class ArtifactInstallerException extends MavenException { + + /** + * + */ + @Serial + private static final long serialVersionUID = 3652561971360586373L; + + /** + * @param message the message of the error + * @param e {@link Exception} + */ + public ArtifactInstallerException(String message, Exception e) { + super(message, e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java new file mode 100644 index 000000000000..6f5a57e0ea2b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * A request for installing one or more artifacts in the local repository. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ArtifactInstallerRequest extends Request { + + @Nonnull + Collection getArtifacts(); + + @Nonnull + static ArtifactInstallerRequestBuilder builder() { + return new ArtifactInstallerRequestBuilder(); + } + + @Nonnull + static ArtifactInstallerRequest build(Session session, Collection artifacts) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .artifacts(requireNonNull(artifacts, "artifacts cannot be null")) + .build(); + } + + @NotThreadSafe + class ArtifactInstallerRequestBuilder { + Session session; + RequestTrace trace; + Collection artifacts = Collections.emptyList(); + + ArtifactInstallerRequestBuilder() {} + + @Nonnull + public ArtifactInstallerRequestBuilder session(@Nonnull Session session) { + this.session = session; + return this; + } + + @Nonnull + public ArtifactInstallerRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + @Nonnull + public ArtifactInstallerRequestBuilder artifacts(@Nullable Collection artifacts) { + this.artifacts = artifacts != null ? artifacts : Collections.emptyList(); + return this; + } + + @Nonnull + public ArtifactInstallerRequest build() { + return new DefaultArtifactInstallerRequest(session, trace, artifacts); + } + + static class DefaultArtifactInstallerRequest extends BaseRequest implements ArtifactInstallerRequest { + + private final Collection artifacts; + + DefaultArtifactInstallerRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull Collection artifacts) { + super(session, trace); + this.artifacts = List.copyOf(requireNonNull(artifacts, "artifacts cannot be null")); + } + + @Nonnull + @Override + public Collection getArtifacts() { + return artifacts; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultArtifactInstallerRequest that && Objects.equals(artifacts, that.artifacts); + } + + @Override + public int hashCode() { + return Objects.hashCode(artifacts); + } + + @Override + public String toString() { + return "ArtifactInstallerRequest[" + "artifacts=" + artifacts + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactManager.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactManager.java new file mode 100644 index 000000000000..8eb1739eafd7 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactManager.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.Optional; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface ArtifactManager extends Service { + + /** + * Returns the path of the file previously associated to this artifact + * or {@code Optional.empty()} if no path has been associated. + */ + @Nonnull + Optional getPath(@Nonnull Artifact artifact); + + /** + * Associates the given file path to the artifact. + */ + void setPath(@Nonnull ProducedArtifact artifact, Path path); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolver.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolver.java new file mode 100644 index 000000000000..c4c413c5470c --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolver.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; +import java.util.List; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; + +/** + * Resolves the artifact, i.e. download the file when required and attach it to the artifact + * + * @since 4.0.0 + */ +@Experimental +public interface ArtifactResolver extends Service { + + /** + * @param request {@link ArtifactResolverRequest} + * @return {@link ArtifactResolverResult} + * @throws ArtifactResolverException in case of an error + * @throws IllegalArgumentException in case of parameter {@code buildingRequest} is {@code null} or + * parameter {@code mavenArtifact} is {@code null} or invalid + */ + ArtifactResolverResult resolve(ArtifactResolverRequest request); + + /** + * Resolves several artifacts from their coordinates. + * + * @param session {@link Session} + * @param coordinates array of {@link ArtifactCoordinates} + * @return {@link ArtifactResolverResult} + * @throws ArtifactResolverException in case of an error. + * @throws IllegalArgumentException in case of parameter {@code buildingRequest} is {@code null} or + * parameter {@code coordinates} is {@code null} or invalid + */ + default ArtifactResolverResult resolve(Session session, Collection coordinates) { + return resolve(ArtifactResolverRequest.build(session, coordinates)); + } + + /** + * Resolves several artifacts from their coordinates. + * + * @param session {@link Session} + * @param repositories the list of remote repositories or {@code null} to use the session repositories + * @param coordinates array of {@link ArtifactCoordinates} + * @return {@link ArtifactResolverResult} + * @throws ArtifactResolverException in case of an error. + * @throws IllegalArgumentException in case of parameter {@code buildingRequest} is {@code null} or + * parameter {@code coordinates} is {@code null} or invalid + */ + default ArtifactResolverResult resolve( + Session session, + Collection coordinates, + List repositories) { + return resolve(ArtifactResolverRequest.build(session, coordinates, repositories)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java new file mode 100644 index 000000000000..22f398c06575 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * + * + * @since 4.0.0 + */ +@Experimental +public class ArtifactResolverException extends MavenException { + + @Serial + private static final long serialVersionUID = 7252294837746943917L; + + private final ArtifactResolverResult result; + + /** + * @param message the message for the exception + * @param e the exception itself + * @param result the resolution result containing detailed information + */ + public ArtifactResolverException(String message, Exception e, ArtifactResolverResult result) { + super(message, e); + this.result = result; + } + + public ArtifactResolverResult getResult() { + return result; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java new file mode 100644 index 000000000000..fb012fab30df --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * A request for resolving an artifact. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ArtifactResolverRequest extends Request { + + @Nonnull + Collection getCoordinates(); + + @Nullable + List getRepositories(); + + @Nonnull + static ArtifactResolverRequestBuilder builder() { + return new ArtifactResolverRequestBuilder(); + } + + @Nonnull + static ArtifactResolverRequest build( + @Nonnull Session session, @Nonnull Collection coordinates) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .coordinates(requireNonNull(coordinates, "coordinates cannot be null")) + .build(); + } + + @Nonnull + static ArtifactResolverRequest build( + @Nonnull Session session, + @Nonnull Collection coordinates, + List repositories) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .coordinates(requireNonNull(coordinates, "coordinates cannot be null")) + .repositories(repositories) + .build(); + } + + @NotThreadSafe + class ArtifactResolverRequestBuilder { + Session session; + RequestTrace trace; + Collection coordinates; + List repositories; + + ArtifactResolverRequestBuilder() {} + + @Nonnull + public ArtifactResolverRequestBuilder session(Session session) { + this.session = session; + return this; + } + + @Nonnull + public ArtifactResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + @Nonnull + public ArtifactResolverRequestBuilder coordinates(Collection coordinates) { + this.coordinates = coordinates; + return this; + } + + @Nonnull + public ArtifactResolverRequestBuilder repositories(List repositories) { + this.repositories = repositories; + return this; + } + + @Nonnull + public ArtifactResolverRequest build() { + return new DefaultArtifactResolverRequest(session, trace, coordinates, repositories); + } + + private static class DefaultArtifactResolverRequest extends BaseRequest + implements ArtifactResolverRequest { + @Nonnull + private final Collection coordinates; + + @Nullable + private final List repositories; + + DefaultArtifactResolverRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull Collection coordinates, + @Nonnull List repositories) { + super(session, trace); + this.coordinates = List.copyOf(requireNonNull(coordinates, "coordinates cannot be null")); + this.repositories = repositories; + } + + @Nonnull + @Override + public Collection getCoordinates() { + return coordinates; + } + + @Nullable + @Override + public List getRepositories() { + return repositories; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultArtifactResolverRequest that + && Objects.equals(coordinates, that.coordinates) + && Objects.equals(repositories, that.repositories); + } + + @Override + public int hashCode() { + return Objects.hash(coordinates, repositories); + } + + @Override + @Nonnull + public String toString() { + return "ArtifactResolverRequest[" + "coordinates=" + + coordinates + ", repositories=" + + repositories + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java new file mode 100644 index 000000000000..5f76c2c7bffa --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.DownloadedArtifact; +import org.apache.maven.api.Repository; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Represents the result of resolving an artifact. + *

+ * This interface provides access to resolved artifacts, their associated paths, and any related exceptions that + * occurred during the resolution process. + *

+ * + * @since 4.0.0 + */ +@Experimental +public interface ArtifactResolverResult extends Result { + + /** + * Returns a collection of resolved artifacts. + * + * @return A collection of {@link DownloadedArtifact} instances representing the resolved artifacts. + */ + @Nonnull + Collection getArtifacts(); + + /** + * Retrieves the file system path associated with a specific artifact. + * + * @param artifact The {@link Artifact} whose path is to be retrieved. + * @return The {@link Path} to the artifact, or {@code null} if unavailable. + */ + @Nullable + Path getPath(@Nonnull Artifact artifact); + + /** + * Returns a mapping of artifact coordinates to their corresponding resolution results. + * + * @return A {@link Map} where keys are {@link ArtifactCoordinates} and values are {@link ResultItem} instances. + */ + @Nonnull + Map getResults(); + + /** + * Retrieves the resolution result for a specific set of artifact coordinates. + * + * @param coordinates The {@link ArtifactCoordinates} identifying the artifact. + * @return The corresponding {@link ResultItem}, or {@code null} if no result exists. + */ + default ResultItem getResult(ArtifactCoordinates coordinates) { + return getResults().get(coordinates); + } + + /** + * Represents an individual resolution result for an artifact. + */ + interface ResultItem { + + /** + * Returns the coordinates of the resolved artifact. + * + * @return The {@link ArtifactCoordinates} of the artifact. + */ + ArtifactCoordinates getCoordinates(); + + /** + * Returns the resolved artifact. + * + * @return The {@link DownloadedArtifact} instance. + */ + DownloadedArtifact getArtifact(); + + /** + * Returns a mapping of repositories to the exceptions encountered while resolving the artifact. + * + * @return A {@link Map} where keys are {@link Repository} instances and values are {@link Exception} instances. + */ + Map> getExceptions(); + + /** + * Returns the repository from which the artifact was resolved. + * + * @return The {@link Repository} instance. + */ + Repository getRepository(); + + /** + * Returns the file system path to the resolved artifact. + * + * @return The {@link Path} to the artifact. + */ + Path getPath(); + + /** + * Indicates whether the requested artifact was resolved. Note that the artifact might have been successfully + * resolved despite {@link #getExceptions()} indicating transfer errors while trying to fetch the artifact from some + * of the specified remote repositories. + * + * @return {@code true} if the artifact was resolved, {@code false} otherwise. + */ + boolean isResolved(); + + /** + * Indicates whether the requested artifact is not present in any of the specified repositories. + * + * @return {@code true} if the artifact is not present in any repository, {@code false} otherwise. + */ + boolean isMissing(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java new file mode 100644 index 000000000000..b5e338466aa7 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +import static java.util.Objects.requireNonNull; + +/** + * Base class for requests. + * + * @since 4.0.0 + */ +@Experimental +abstract class BaseRequest implements Request { + + private final S session; + private final RequestTrace trace; + + protected BaseRequest(@Nonnull S session) { + this(session, null); + } + + protected BaseRequest(@Nonnull S session, RequestTrace trace) { + this.session = requireNonNull(session, "session cannot be null"); + this.trace = trace; + } + + @Nonnull + @Override + public S getSession() { + return session; + } + + @Override + public RequestTrace getTrace() { + return trace; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/BuilderProblem.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/BuilderProblem.java new file mode 100644 index 000000000000..9e4cb45ae9dc --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/BuilderProblem.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Describes a problem that was encountered during project building. A problem can either be an exception that was + * thrown or a simple string message. In addition, a problem carries a hint about its source. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface BuilderProblem { + + /** + * Gets the hint about the source of the problem. While the syntax of this hint is unspecified and depends on the + * creator of the problem, the general expectation is that the hint provides sufficient information to the user to + * track the problem back to its origin. A concrete example for such a source hint can be the file path or URL from + * which the settings were read. + * + * @return the hint about the source of the problem or an empty string if unknown, never {@code null} + */ + @Nonnull + String getSource(); + + /** + * Gets the one-based index of the line containing the problem. The line number should refer to some text file that + * is given by {@link #getSource()}. + * + * @return the one-based index of the line containing the problem or a non-positive value if unknown + */ + int getLineNumber(); + + /** + * Gets the one-based index of the column containing the problem. The column number should refer to some text file + * that is given by {@link #getSource()}. + * + * @return the one-based index of the column containing the problem or non-positive value if unknown + */ + int getColumnNumber(); + + /** + * Gets the location of the problem. The location is a user-friendly combination of the values from + * {@link #getSource()}, {@link #getLineNumber()} and {@link #getColumnNumber()}. The exact syntax of the returned + * value is undefined. + * + * @return the location of the problem, never {@code null} + */ + @Nonnull + String getLocation(); + + /** + * Gets the exception that caused this problem (if any). + * + * @return the exception that caused this problem or {@code null} if not applicable + */ + @Nullable + Exception getException(); + + /** + * Gets the message that describes this problem. + * + * @return the message describing this problem, never {@code null} + */ + @Nonnull + String getMessage(); + + /** + * Gets the severity level of this problem. + * + * @return the severity level of this problem, never {@code null} + */ + @Nonnull + Severity getSeverity(); + + /** + * The different severity levels for a problem, in decreasing order. + * + * @since 4.0.0 + */ + @Experimental + enum Severity { + FATAL, // + ERROR, // + WARNING // + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmService.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmService.java new file mode 100644 index 000000000000..7344cc8d1046 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmService.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Checksum algorithms service. + * + * @since 4.0.0 + */ +@Experimental +public interface ChecksumAlgorithmService extends Service { + + /** + * Returns immutable collection of all supported algorithm names. + */ + @Nonnull + Collection getChecksumAlgorithmNames(); + + /** + * Returns {@link ChecksumAlgorithm} for given algorithm name, or throws if algorithm not supported. + * + * @throws ChecksumAlgorithmServiceException if asked algorithm name is not supported. + * @throws NullPointerException if passed in name is {@code null}. + */ + @Nonnull + ChecksumAlgorithm select(@Nonnull String algorithmName); + + /** + * Returns a collection of {@link ChecksumAlgorithm} in same order as algorithm names are ordered, or throws if + * any of the algorithm name is not supported. The returned collection has equal count of elements as passed in + * collection of names, and if names contains duplicated elements, the returned list of algorithms will have + * duplicates as well. + * + * @throws ChecksumAlgorithmServiceException if any asked algorithm name is not supported. + * @throws NullPointerException if passed in list of names is {@code null}. + */ + @Nonnull + Collection select(@Nonnull Collection algorithmNames); + + /** + * Calculates checksums for specified data. + * + * @param data The content for which to calculate checksums, must not be {@code null}. + * @param algorithms The checksum algorithms to use, must not be {@code null}. + * @return The calculated checksums, indexed by algorithms, never {@code null}. + * @throws NullPointerException if passed in any parameter is {@code null}. + */ + @Nonnull + Map calculate(@Nonnull byte[] data, @Nonnull Collection algorithms); + + /** + * Calculates checksums for specified data. + * + * @param data The content for which to calculate checksums, must not be {@code null}. + * @param algorithms The checksum algorithms to use, must not be {@code null}. + * @return The calculated checksums, indexed by algorithms, never {@code null}. + * @throws NullPointerException if passed in any parameter is {@code null}. + */ + @Nonnull + Map calculate( + @Nonnull ByteBuffer data, @Nonnull Collection algorithms); + + /** + * Calculates checksums for specified file. + * + * @param file The file for which to calculate checksums, must not be {@code null}. + * @param algorithms The checksum algorithms to use, must not be {@code null}. + * @return The calculated checksums, indexed by algorithms, never {@code null}. + * @throws NullPointerException if passed in any parameter is {@code null}. + * @throws IOException In case of any IO problem. + */ + @Nonnull + Map calculate(@Nonnull Path file, @Nonnull Collection algorithms) + throws IOException; + + /** + * Calculates checksums for specified stream. Upon this method returns, the stream will be depleted (fully read) + * but not closed. + * + * @param stream The stream for which to calculate checksums, must not be {@code null}. + * @param algorithms The checksum algorithms to use, must not be {@code null}. + * @return The calculated checksums, indexed by algorithms, never {@code null}. + * @throws NullPointerException if passed in any parameter is {@code null}. + * @throws IOException In case of any IO problem. + */ + @Nonnull + Map calculate( + @Nonnull InputStream stream, @Nonnull Collection algorithms) throws IOException; + + /** + * The checksum algorithm. + */ + interface ChecksumAlgorithm { + /** + * Returns the algorithm name, usually used as key, never {@code null} value. The name is a standard name of + * algorithm (if applicable) or any other designator that is algorithm commonly referred with. Example: "SHA-1". + */ + @Nonnull + String getName(); + + /** + * Returns the file extension to be used for given checksum file (without leading dot), never {@code null}. The + * extension should be file and URL path friendly, and may differ from algorithm name. + * The checksum extension SHOULD NOT contain dot (".") character. + * Example: "sha1". + */ + @Nonnull + String getFileExtension(); + + /** + * Each invocation of this method returns a new instance of calculator, never {@code null} value. + */ + @Nonnull + ChecksumCalculator getCalculator(); + } + + /** + * The checksum calculator. + */ + interface ChecksumCalculator { + /** + * Updates the checksum algorithm inner state with input. + * + * @throws NullPointerException if passed in buffer is {@code null}. + */ + void update(@Nonnull ByteBuffer input); + + /** + * Returns the algorithm end result as string, never {@code null}. After invoking this method, this instance should + * be discarded and not reused. For new checksum calculation you have to get new instance. + */ + @Nonnull + String checksum(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java new file mode 100644 index 000000000000..a42546e8285f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +@Experimental +public class ChecksumAlgorithmServiceException extends MavenException { + + @Serial + private static final long serialVersionUID = 1201171469179367694L; + + public ChecksumAlgorithmServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactory.java new file mode 100644 index 000000000000..29b06c45d3b6 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactory.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.DependencyCoordinates; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Dependency; +import org.apache.maven.api.model.Plugin; +import org.apache.maven.api.model.ReportPlugin; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface DependencyCoordinatesFactory extends Service { + + /** + * Creates a new {@link DependencyCoordinates} object from the request. + * + * @param request the request containing the various data + * @return a new {@link DependencyCoordinates} object + * + * @throws IllegalArgumentException if {@code request} is null or + * if {@code request.getSession()} is null or invalid + */ + @Nonnull + DependencyCoordinates create(@Nonnull DependencyCoordinatesFactoryRequest request); + + @Nonnull + default DependencyCoordinates create(@Nonnull Session session, @Nonnull ArtifactCoordinates coordinates) { + return create(DependencyCoordinatesFactoryRequest.build(session, coordinates)); + } + + @Nonnull + default DependencyCoordinates create( + @Nonnull Session session, @Nonnull org.apache.maven.api.Dependency dependency) { + return create(DependencyCoordinatesFactoryRequest.build(session, dependency)); + } + + @Nonnull + default DependencyCoordinates create(@Nonnull Session session, Dependency dependency) { + return create(DependencyCoordinatesFactoryRequest.build( + session, + dependency.getGroupId(), + dependency.getArtifactId(), + dependency.getVersion(), + dependency.getClassifier(), + null, + dependency.getType())); + } + + @Nonnull + default DependencyCoordinates create(@Nonnull Session session, Plugin plugin) { + // TODO: hard coded string + return create(DependencyCoordinatesFactoryRequest.build( + session, plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion(), null, null, "maven-plugin")); + } + + @Nonnull + default DependencyCoordinates create(@Nonnull Session session, ReportPlugin reportPlugin) { + // TODO: hard coded string + return create(DependencyCoordinatesFactoryRequest.build( + session, + reportPlugin.getGroupId(), + reportPlugin.getArtifactId(), + reportPlugin.getVersion(), + null, + null, + "maven-plugin")); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java new file mode 100644 index 000000000000..c0314b2078b5 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.Dependency; +import org.apache.maven.api.Exclusion; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface DependencyCoordinatesFactoryRequest extends ArtifactCoordinatesFactoryRequest { + + String getScope(); + + boolean isOptional(); + + @Nonnull + Collection getExclusions(); + + @Nonnull + static DependencyCoordinatesFactoryRequest build( + @Nonnull Session session, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type) { + return DependencyCoordinatesFactoryRequest.builder() + .session(requireNonNull(session, "session cannot be null")) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .classifier(classifier) + .extension(extension) + .type(type) + .build(); + } + + @Nonnull + static DependencyCoordinatesFactoryRequest build( + @Nonnull Session session, @Nonnull ArtifactCoordinates coordinates) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .groupId(requireNonNull(coordinates, "coordinates cannot be null") + .getGroupId()) + .artifactId(coordinates.getArtifactId()) + .version(coordinates.getVersionConstraint().toString()) + .classifier(coordinates.getClassifier()) + .extension(coordinates.getExtension()) + .build(); + } + + @Nonnull + static DependencyCoordinatesFactoryRequest build(@Nonnull Session session, @Nonnull Dependency dependency) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .groupId(requireNonNull(dependency, "dependency").getGroupId()) + .artifactId(dependency.getArtifactId()) + .version(dependency.getVersion().toString()) + .classifier(dependency.getClassifier()) + .extension(dependency.getExtension()) + .type(dependency.getType().id()) + .scope(dependency.getScope().id()) + .optional(dependency.isOptional()) + .build(); + } + + @Nonnull + static DependencyCoordinatesFactoryRequestBuilder builder() { + return new DependencyCoordinatesFactoryRequestBuilder(); + } + + @NotThreadSafe + class DependencyCoordinatesFactoryRequestBuilder { + private Session session; + private RequestTrace trace; + private String groupId; + private String artifactId; + private String version; + private String classifier; + private String extension; + private String type; + private String coordinateString; + private String scope; + private boolean optional; + private Collection exclusions = Collections.emptyList(); + + DependencyCoordinatesFactoryRequestBuilder() {} + + public DependencyCoordinatesFactoryRequestBuilder session(Session session) { + this.session = session; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder groupId(String groupId) { + this.groupId = groupId; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder artifactId(String artifactId) { + this.artifactId = artifactId; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder version(String version) { + this.version = version; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder classifier(String classifier) { + this.classifier = classifier; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder extension(String extension) { + this.extension = extension; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder type(String type) { + this.type = type; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder coordinateString(String coordinateString) { + this.coordinateString = coordinateString; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder scope(String scope) { + this.scope = scope; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder optional(boolean optional) { + this.optional = optional; + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder exclusions(Collection exclusions) { + if (exclusions != null) { + if (this.exclusions.isEmpty()) { + this.exclusions = new ArrayList<>(); + } + this.exclusions.addAll(exclusions); + } + return this; + } + + public DependencyCoordinatesFactoryRequestBuilder exclusion(Exclusion exclusion) { + if (exclusion != null) { + if (this.exclusions.isEmpty()) { + this.exclusions = new ArrayList<>(); + } + this.exclusions.add(exclusion); + } + return this; + } + + public DependencyCoordinatesFactoryRequest build() { + return new DefaultDependencyCoordinatesFactoryRequest( + session, + trace, + groupId, + artifactId, + version, + classifier, + extension, + type, + coordinateString, + scope, + optional, + exclusions); + } + + private static class DefaultDependencyCoordinatesFactoryRequest extends BaseRequest + implements DependencyCoordinatesFactoryRequest { + private final String groupId; + private final String artifactId; + private final String version; + private final String classifier; + private final String extension; + private final String type; + private final String coordinateString; + private final String scope; + private final boolean optional; + private final Collection exclusions; + + @SuppressWarnings("checkstyle:ParameterNumber") + private DefaultDependencyCoordinatesFactoryRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + String groupId, + String artifactId, + String version, + String classifier, + String extension, + String type, + String coordinateString, + String scope, + boolean optional, + Collection exclusions) { + super(session, trace); + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classifier = classifier; + this.extension = extension; + this.type = type; + this.coordinateString = coordinateString; + this.scope = scope; + this.optional = optional; + this.exclusions = exclusions; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public String getArtifactId() { + return artifactId; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getClassifier() { + return classifier; + } + + @Override + public String getExtension() { + return extension; + } + + @Override + public String getType() { + return type; + } + + @Override + public String getCoordinatesString() { + return coordinateString; + } + + @Override + public String getScope() { + return scope; + } + + @Override + public boolean isOptional() { + return optional; + } + + @Nonnull + @Override + public Collection getExclusions() { + return exclusions; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultDependencyCoordinatesFactoryRequest that + && optional == that.optional + && Objects.equals(groupId, that.groupId) + && Objects.equals(artifactId, that.artifactId) + && Objects.equals(version, that.version) + && Objects.equals(classifier, that.classifier) + && Objects.equals(extension, that.extension) + && Objects.equals(type, that.type) + && Objects.equals(coordinateString, that.coordinateString) + && Objects.equals(scope, that.scope) + && Objects.equals(exclusions, that.exclusions); + } + + @Override + public int hashCode() { + return Objects.hash( + groupId, + artifactId, + version, + classifier, + extension, + type, + coordinateString, + scope, + optional, + exclusions); + } + + @Override + public String toString() { + return "DependencyCoordinatesFactoryRequest[" + "groupId='" + + groupId + '\'' + ", artifactId='" + + artifactId + '\'' + ", version='" + + version + '\'' + ", classifier='" + + classifier + '\'' + ", extension='" + + extension + '\'' + ", type='" + + type + '\'' + ", coordinateString='" + + coordinateString + '\'' + ", scope='" + + scope + '\'' + ", optional=" + + optional + ", exclusions=" + + exclusions + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java new file mode 100644 index 000000000000..0473cce53708 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.DependencyCoordinates; +import org.apache.maven.api.Node; +import org.apache.maven.api.PathScope; +import org.apache.maven.api.Project; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Collects, flattens and resolves dependencies. + */ +@Experimental +public interface DependencyResolver extends Service { + + /** + * Collects the transitive dependencies of some artifacts and builds a dependency graph for the given path scope. + * Note that this operation is only concerned about determining the coordinates of the transitive dependencies and + * does not actually resolve the artifact files. + * + * @param session the {@link Session}, must not be {@code null} + * @param root the Maven Dependency, must not be {@code null} + * @param scope the {link PathScope} to collect dependencies, must not be {@code null} + * @return the collection result, never {@code null} + * @throws DependencyResolverException if the dependency tree could not be built + * @throws IllegalArgumentException if an argument is null or invalid + * @see #collect(DependencyResolverRequest) + */ + @Nonnull + default DependencyResolverResult collect( + @Nonnull Session session, @Nonnull DependencyCoordinates root, @Nonnull PathScope scope) { + return collect( + DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.COLLECT, root, scope)); + } + + /** + * Collects the transitive dependencies of some artifacts and builds a dependency graph for the given path scope. + * Note that this operation is only concerned about determining the coordinates of the transitive dependencies and + * does not actually resolve the artifact files. + * + * @param session the {@link Session}, must not be {@code null} + * @param project the {@link Project}, must not be {@code null} + * @param scope the {link PathScope} to collect dependencies, must not be {@code null} + * @return the collection result, never {@code null} + * @throws DependencyResolverException if the dependency tree could not be built + * @throws IllegalArgumentException if an argument is null or invalid + * @see #collect(DependencyResolverRequest) + */ + @Nonnull + default DependencyResolverResult collect( + @Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) { + return collect(DependencyResolverRequest.build( + session, DependencyResolverRequest.RequestType.COLLECT, project, scope)); + } + + /** + * Collects the transitive dependencies of some artifacts and builds a dependency graph for the given path scope. + * Note that this operation is only concerned about determining the coordinates of the transitive dependencies and + * does not actually resolve the artifact files. + * + * @param session the {@link Session}, must not be {@code null} + * @param artifact the {@link Artifact}, must not be {@code null} + * @param scope the {link PathScope} to collect dependencies, must not be {@code null} + * @return the collection result, never {@code null} + * @throws DependencyResolverException if the dependency tree could not be built + * @throws IllegalArgumentException if an argument is null or invalid + * @see #collect(DependencyResolverRequest) + */ + @Nonnull + default DependencyResolverResult collect( + @Nonnull Session session, @Nonnull Artifact artifact, @Nonnull PathScope scope) { + return collect(DependencyResolverRequest.build( + session, DependencyResolverRequest.RequestType.COLLECT, artifact, scope)); + } + + /** + * Collects the transitive dependencies and builds a dependency graph. + * Note that this operation is only concerned about determining the coordinates of the + * transitive dependencies and does not actually resolve the artifact files. + * + * @param request the dependency collection request, must not be {@code null} + * @return the collection result, never {@code null} + * @throws DependencyResolverException if the dependency tree could not be built + * @throws IllegalArgumentException if an argument is null or invalid + * + * @see DependencyResolver#collect(Session, Project, PathScope) + * @see DependencyResolver#collect(Session, DependencyCoordinates, PathScope) + * @see DependencyResolver#collect(Session, Artifact, PathScope) + */ + @Nonnull + default DependencyResolverResult collect(@Nonnull DependencyResolverRequest request) { + if (request.getRequestType() != DependencyResolverRequest.RequestType.COLLECT) { + throw new IllegalArgumentException("requestType should be COLLECT when calling collect()"); + } + return resolve(request); + } + + /** + * Flattens a list of nodes. + * Note that the {@code PathScope} argument should usually be null as the dependency tree has been + * filtered during collection for the appropriate scope. + * + * @param session the {@link Session}, must not be {@code null} + * @param node the {@link Node} to flatten, must not be {@code null} + * @param scope an optional {@link PathScope} to filter out dependencies + * @return the flattened list of node + * @throws DependencyResolverException + */ + List flatten(@Nonnull Session session, @Nonnull Node node, @Nullable PathScope scope) + throws DependencyResolverException; + + @Nonnull + default DependencyResolverResult flatten( + @Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) { + return flatten(DependencyResolverRequest.build( + session, DependencyResolverRequest.RequestType.FLATTEN, project, scope)); + } + + @Nonnull + default DependencyResolverResult flatten(@Nonnull DependencyResolverRequest request) { + if (request.getRequestType() != DependencyResolverRequest.RequestType.FLATTEN) { + throw new IllegalArgumentException("requestType should be FLATTEN when calling flatten()"); + } + return resolve(request); + } + + @Nonnull + default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull Project project) { + return resolve( + DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.RESOLVE, project)); + } + + @Nonnull + default DependencyResolverResult resolve( + @Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) { + return resolve(DependencyResolverRequest.build( + session, DependencyResolverRequest.RequestType.RESOLVE, project, scope)); + } + + @Nonnull + default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull DependencyCoordinates dependency) { + return resolve( + DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.RESOLVE, dependency)); + } + + @Nonnull + default DependencyResolverResult resolve( + @Nonnull Session session, @Nonnull DependencyCoordinates dependency, @Nonnull PathScope scope) { + return resolve(DependencyResolverRequest.build( + session, DependencyResolverRequest.RequestType.RESOLVE, dependency, scope)); + } + + @Nonnull + default DependencyResolverResult resolve( + @Nonnull Session session, @Nonnull List dependencies) { + return resolve( + DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.RESOLVE, dependencies)); + } + + @Nonnull + default DependencyResolverResult resolve( + @Nonnull Session session, @Nonnull List dependencies, @Nonnull PathScope scope) { + return resolve(DependencyResolverRequest.build( + session, DependencyResolverRequest.RequestType.RESOLVE, dependencies, scope)); + } + + /** + * This method collects, flattens and resolves the dependencies. + * + * @param request the request to resolve + * @return the result of the resolution + * @throws DependencyResolverException + * @throws ArtifactResolverException + * + * @see DependencyResolver#collect(DependencyResolverRequest) + * @see #flatten(Session, Node, PathScope) + * @see ArtifactResolver#resolve(ArtifactResolverRequest) + */ + DependencyResolverResult resolve(DependencyResolverRequest request) + throws DependencyResolverException, ArtifactResolverException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java new file mode 100644 index 000000000000..3bc03d119016 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +@Experimental +public class DependencyResolverException extends MavenException { + + @Serial + private static final long serialVersionUID = 1101171569179057614L; + + public DependencyResolverException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java new file mode 100644 index 000000000000..f419d7ff60a7 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java @@ -0,0 +1,549 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.DependencyCoordinates; +import org.apache.maven.api.JavaPathType; +import org.apache.maven.api.PathScope; +import org.apache.maven.api.PathType; +import org.apache.maven.api.Project; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to + * create a dependency graph. First, only the root dependency can be given. Second, a root dependency and direct + * dependencies can be specified in which case the specified direct dependencies are merged with the direct dependencies + * retrieved from the artifact descriptor of the root dependency. And last, only direct dependencies can be specified in + * which case the root node of the resulting graph has no associated dependency. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface DependencyResolverRequest extends Request { + + enum RequestType { + COLLECT, + FLATTEN, + RESOLVE + } + + @Nonnull + RequestType getRequestType(); + + @Nonnull + Optional getProject(); + + @Nonnull + Optional getRootArtifact(); + + @Nonnull + Optional getRoot(); + + @Nonnull + Collection getDependencies(); + + @Nonnull + Collection getManagedDependencies(); + + boolean getVerbose(); + + @Nonnull + PathScope getPathScope(); + + /** + * Returns a filter for the types of path (class-path, module-path, …) accepted by the tool. + * For example, if a Java tools accepts only class-path elements, then the filter should return + * {@code true} for {@link JavaPathType#CLASSES} and {@code false} for {@link JavaPathType#MODULES}. + * If no filter is explicitly set, then the default is a filter accepting everything. + * + * @return a filter for the types of path (class-path, module-path, …) accepted by the tool + */ + @Nullable + Predicate getPathTypeFilter(); + + @Nullable + List getRepositories(); + + @Nonnull + static DependencyResolverRequestBuilder builder() { + return new DependencyResolverRequestBuilder(); + } + + @Nonnull + static DependencyResolverRequest build(Session session, RequestType requestType, Artifact rootArtifact) { + return build(session, requestType, rootArtifact, PathScope.MAIN_RUNTIME); + } + + @Nonnull + static DependencyResolverRequest build( + Session session, RequestType requestType, Artifact rootArtifact, PathScope scope) { + return new DependencyResolverRequestBuilder() + .session(session) + .requestType(requestType) + .rootArtifact(rootArtifact) + .pathScope(scope) + .build(); + } + + @Nonnull + static DependencyResolverRequest build(Session session, RequestType requestType, Project project) { + return build(session, requestType, project, PathScope.MAIN_RUNTIME); + } + + @Nonnull + static DependencyResolverRequest build(Session session, RequestType requestType, Project project, PathScope scope) { + return new DependencyResolverRequestBuilder() + .session(session) + .requestType(requestType) + .project(project) + .pathScope(scope) + .build(); + } + + @Nonnull + static DependencyResolverRequest build(Session session, RequestType requestType, DependencyCoordinates dependency) { + return build(session, requestType, dependency, PathScope.MAIN_RUNTIME); + } + + @Nonnull + static DependencyResolverRequest build( + Session session, RequestType requestType, DependencyCoordinates dependency, PathScope scope) { + return new DependencyResolverRequestBuilder() + .session(session) + .requestType(requestType) + .dependency(dependency) + .pathScope(scope) + .build(); + } + + @Nonnull + static DependencyResolverRequest build( + Session session, RequestType requestType, List dependencies) { + return build(session, requestType, dependencies, PathScope.MAIN_RUNTIME); + } + + @Nonnull + static DependencyResolverRequest build( + Session session, RequestType requestType, List dependencies, PathScope scope) { + return new DependencyResolverRequestBuilder() + .session(session) + .requestType(requestType) + .dependencies(dependencies) + .pathScope(scope) + .build(); + } + + @NotThreadSafe + class DependencyResolverRequestBuilder { + + Session session; + RequestTrace trace; + RequestType requestType; + Project project; + Artifact rootArtifact; + DependencyCoordinates root; + List dependencies = Collections.emptyList(); + List managedDependencies = Collections.emptyList(); + boolean verbose; + PathScope pathScope; + Predicate pathTypeFilter; + List repositories; + + DependencyResolverRequestBuilder() {} + + @Nonnull + public DependencyResolverRequestBuilder session(@Nonnull Session session) { + this.session = session; + return this; + } + + @Nonnull + public DependencyResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + @Nonnull + public DependencyResolverRequestBuilder requestType(@Nonnull RequestType requestType) { + this.requestType = requestType; + return this; + } + + @Nonnull + public DependencyResolverRequestBuilder project(@Nullable Project project) { + this.project = project; + return this; + } + + /** + * Sets the root artifact for the dependency graph. + * This must not be confused with {@link #root(DependencyCoordinates)}: The root dependency, like any + * other specified dependency, will be subject to dependency collection/resolution, i.e. should have an artifact + * descriptor and a corresponding artifact file. The root artifact on the other hand is only used + * as a label for the root node of the graph in case no root dependency was specified. As such, the configured + * root artifact is ignored if {@link #root(DependencyCoordinates)} has been set. + * + * @param rootArtifact the root artifact for the dependency graph, may be {@code null} + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder rootArtifact(@Nullable Artifact rootArtifact) { + this.rootArtifact = rootArtifact; + return this; + } + + /** + * @param root The root dependency + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder root(@Nonnull DependencyCoordinates root) { + this.root = root; + return this; + } + + /** + * Sets the direct dependencies. If both a root dependency and direct dependencies are given in the request, the + * direct dependencies from the request will be merged with the direct dependencies from the root dependency's + * artifact descriptor, giving higher priority to the dependencies from the request. + * + * @param dependencies the direct dependencies, may be {@code null} + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder dependencies(@Nullable List dependencies) { + this.dependencies = (dependencies != null) ? dependencies : Collections.emptyList(); + return this; + } + + /** + * Adds the specified direct dependency. + * + * @param dependency the dependency to add, may be {@code null} + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder dependency(@Nullable DependencyCoordinates dependency) { + if (dependency != null) { + if (this.dependencies.isEmpty()) { + this.dependencies = new ArrayList<>(); + } + this.dependencies.add(dependency); + } + return this; + } + + /** + * Sets the dependency management to apply to transitive dependencies. To clarify, this management does not + * apply to + * the direct dependencies of the root node. + * + * @param managedDependencies the dependency management, may be {@code null} + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder managedDependencies( + @Nullable List managedDependencies) { + this.managedDependencies = (managedDependencies != null) ? managedDependencies : Collections.emptyList(); + return this; + } + + /** + * Adds the specified managed dependency. + * + * @param managedDependency The managed dependency to add, may be {@code null} in which case the call + * will have no effect. + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder managedDependency(@Nullable DependencyCoordinates managedDependency) { + if (managedDependency != null) { + if (this.managedDependencies.isEmpty()) { + this.managedDependencies = new ArrayList<>(); + } + this.managedDependencies.add(managedDependency); + } + return this; + } + + /** + * Specifies that the collection should be verbose. + * + * @param verbose whether the collection should be verbose or not + * @return this request for chaining, never {@code null} + */ + @Nonnull + public DependencyResolverRequestBuilder verbose(boolean verbose) { + this.verbose = verbose; + return this; + } + + @Nonnull + public DependencyResolverRequestBuilder pathScope(@Nullable PathScope pathScope) { + this.pathScope = pathScope; + return this; + } + + /** + * Filters the types of paths to include in the result. + * The result will contain only the paths of types for which the predicate returned {@code true}. + * It is recommended to apply a filter for retaining only the types of paths of interest, + * because it can resolve ambiguities when a path could be of many types. + * + * @param pathTypeFilter predicate evaluating whether a path type should be included in the result + * @return {@code this} for method call chaining + */ + @Nonnull + public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Predicate pathTypeFilter) { + this.pathTypeFilter = pathTypeFilter; + return this; + } + + /** + * Specifies the type of paths to include in the result. This is a convenience method for + * {@link #pathTypeFilter(Predicate)} using {@link Collection#contains(Object)} as the filter. + * + * @param desiredTypes the type of paths to include in the result + * @return {@code this} for method call chaining + */ + @Nonnull + public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Collection desiredTypes) { + return pathTypeFilter(desiredTypes::contains); + } + + @Nonnull + public DependencyResolverRequestBuilder repositories(@Nonnull List repositories) { + this.repositories = repositories; + return this; + } + + @Nonnull + public DependencyResolverRequest build() { + return new DefaultDependencyResolverRequest( + session, + trace, + requestType, + project, + rootArtifact, + root, + dependencies, + managedDependencies, + verbose, + pathScope, + pathTypeFilter, + repositories); + } + + static class DefaultDependencyResolverRequest extends BaseRequest + implements DependencyResolverRequest { + + static final class AlwaysTrueFilter implements Predicate { + @Override + public boolean test(PathType pathType) { + return true; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof AlwaysTrueFilter; + } + + @Override + public int hashCode() { + return AlwaysTrueFilter.class.hashCode(); + } + + @Override + public String toString() { + return "AlwaysTrueFilter[]"; + } + } + + private static final Predicate DEFAULT_FILTER = new AlwaysTrueFilter(); + + private final RequestType requestType; + private final Project project; + private final Artifact rootArtifact; + private final DependencyCoordinates root; + private final Collection dependencies; + private final Collection managedDependencies; + private final boolean verbose; + private final PathScope pathScope; + private final Predicate pathTypeFilter; + private final List repositories; + + /** + * Creates a request with the specified properties. + * + * @param session {@link Session} + * @param rootArtifact The root dependency whose transitive dependencies should be collected, may be {@code + * null}. + */ + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultDependencyResolverRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull RequestType requestType, + @Nullable Project project, + @Nullable Artifact rootArtifact, + @Nullable DependencyCoordinates root, + @Nonnull Collection dependencies, + @Nonnull Collection managedDependencies, + boolean verbose, + @Nullable PathScope pathScope, + @Nullable Predicate pathTypeFilter, + @Nullable List repositories) { + super(session, trace); + this.requestType = requireNonNull(requestType, "requestType cannot be null"); + this.project = project; + this.rootArtifact = rootArtifact; + this.root = root; + this.dependencies = List.copyOf(requireNonNull(dependencies, "dependencies cannot be null")); + this.managedDependencies = + List.copyOf(requireNonNull(managedDependencies, "managedDependencies cannot be null")); + this.verbose = verbose; + this.pathScope = requireNonNull(pathScope, "pathScope cannot be null"); + this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : DEFAULT_FILTER; + this.repositories = repositories; + if (verbose && requestType != RequestType.COLLECT) { + throw new IllegalArgumentException("verbose cannot only be true when collecting dependencies"); + } + } + + @Nonnull + @Override + public RequestType getRequestType() { + return requestType; + } + + @Nonnull + @Override + public Optional getProject() { + return Optional.ofNullable(project); + } + + @Nonnull + @Override + public Optional getRootArtifact() { + return Optional.ofNullable(rootArtifact); + } + + @Nonnull + @Override + public Optional getRoot() { + return Optional.ofNullable(root); + } + + @Nonnull + @Override + public Collection getDependencies() { + return dependencies; + } + + @Nonnull + @Override + public Collection getManagedDependencies() { + return managedDependencies; + } + + @Override + public boolean getVerbose() { + return verbose; + } + + @Override + public PathScope getPathScope() { + return pathScope; + } + + @Override + public Predicate getPathTypeFilter() { + return pathTypeFilter; + } + + @Override + public List getRepositories() { + return repositories; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultDependencyResolverRequest that + && verbose == that.verbose + && requestType == that.requestType + && Objects.equals(project, that.project) + && Objects.equals(rootArtifact, that.rootArtifact) + && Objects.equals(root, that.root) + && Objects.equals(dependencies, that.dependencies) + && Objects.equals(managedDependencies, that.managedDependencies) + && Objects.equals(pathScope, that.pathScope) + && Objects.equals(pathTypeFilter, that.pathTypeFilter) + && Objects.equals(repositories, that.repositories); + } + + @Override + public int hashCode() { + return Objects.hash( + requestType, + project, + rootArtifact, + root, + dependencies, + managedDependencies, + verbose, + pathScope, + pathTypeFilter, + repositories); + } + + @Override + public String toString() { + return "DependencyResolverRequest[" + "requestType=" + + requestType + ", project=" + + project + ", rootArtifact=" + + rootArtifact + ", root=" + + root + ", dependencies=" + + dependencies + ", managedDependencies=" + + managedDependencies + ", verbose=" + + verbose + ", pathScope=" + + pathScope + ", pathTypeFilter=" + + pathTypeFilter + ", repositories=" + + repositories + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java new file mode 100644 index 000000000000..456cd6b505be --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.IOException; +import java.lang.module.ModuleDescriptor; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.Dependency; +import org.apache.maven.api.DependencyScope; +import org.apache.maven.api.JavaPathType; +import org.apache.maven.api.Node; +import org.apache.maven.api.PathType; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * The result of a dependency resolution request. + * + * @since 4.0.0 + * @see DependencyResolver#resolve(DependencyResolverRequest) + */ +@Experimental +public interface DependencyResolverResult extends Result { + + /** + * Gets the exceptions that occurred while building the dependency graph. + * + * @return the exceptions that occurred, never {@code null} + */ + @Nonnull + List getExceptions(); + + /** + * Gets the root node of the dependency graph. + * + * @return the root node of the dependency graph or {@code null} if none + */ + @Nullable + Node getRoot(); + + /** + * The ordered list of the flattened dependency nodes. + * + * @return the ordered list of the flattened dependency nodes + */ + @Nonnull + List getNodes(); + + /** + * Returns the file paths of all dependencies, regardless of which tool option those paths should be placed on. + * The returned list may contain a mix of Java class path, Java module path, and other types of path elements. + * This collection has the same content as {@code getDependencies.values()} except that it does not contain + * null elements. + * + * @return the paths of all dependencies + */ + @Nonnull + List getPaths(); + + /** + * Returns the file paths of all dependencies and their assignments to different paths. + * The {@link PathType} keys identify, for example, {@code --class-path} or {@code --module-path} options. + * In the case of Java tools, the map may also contain {@code --patch-module} options, which are + * {@linkplain org.apache.maven.api.JavaPathType#patchModule(String) handled in a special way}. + * + *

Design note

+ * All types of path are determined together because they are sometime mutually exclusive. + * For example, an artifact of type {@value org.apache.maven.api.Type#JAR} can be placed + * either on the class-path or on the module-path. The project needs to make a choice + * (possibly using heuristic rules), then add the dependency in only one of the paths + * identified by {@link PathType}. + * + * @return file paths to place on the different tool options + */ + @Nonnull + Map> getDispatchedPaths(); + + /** + * {@return all dependencies associated with their paths} + * Some dependencies may be associated with a null value if there is no path available. + */ + @Nonnull + Map getDependencies(); + + /** + * Returns the Java module name of the dependency at the given path. + * The given dependency should be one of the paths returned by {@link #getDependencies()}. + * The module name is extracted from the {@code module-info.class} file if present, otherwise from + * the {@code "Automatic-Module-Name"} attribute of the {@code META-INF/MANIFEST.MF} file if present. + * + *

A typical usage is to invoke this method for all dependencies having a + * {@link DependencyScope#TEST TEST} or {@link DependencyScope#TEST_ONLY TEST_ONLY} + * {@linkplain Dependency#getScope() scope}. An {@code --add-reads} option may need + * to be generated for compiling and running the test classes that use such dependencies.

+ * + * @param dependency path to the dependency for which to get the module name + * @return module name of the dependency at the given path, or empty if the dependency is not modular + * @throws IOException if the module information of the specified dependency cannot be read + */ + Optional getModuleName(@Nonnull Path dependency) throws IOException; + + /** + * Returns the Java module descriptor of the dependency at the given path. + * The given dependency should be one of the paths returned by {@link #getDependencies()}. + * The module descriptor is extracted from the {@code module-info.class} file if present. + * + *

{@link #getModuleName(Path)} is preferred when only the module name is desired, + * because a name may be present even if the descriptor is absent. This method is for + * cases when more information is desired, such as the set of exported packages.

+ * + * @param dependency path to the dependency for which to get the module name + * @return module name of the dependency at the given path, or empty if the dependency is not modular + * @throws IOException if the module information of the specified dependency cannot be read + */ + Optional getModuleDescriptor(@Nonnull Path dependency) throws IOException; + + /** + * If the module path contains at least one filename-based auto-module, prepares a warning message. + * The module path is the collection of dependencies associated with {@link JavaPathType#MODULES}. + * It is caller's responsibility to send the message to a logger. + * + * @return warning message if at least one filename-based auto-module was found + */ + Optional warningForFilenameBasedAutomodules(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ExtensibleEnumRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ExtensibleEnumRegistry.java new file mode 100644 index 000000000000..06d9726cacd1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ExtensibleEnumRegistry.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Optional; + +import org.apache.maven.api.ExtensibleEnum; +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Registry for extensible enum values that allows looking up enum instances by their identifiers. + *

+ * This service provides access to all registered instances of a specific extensible enum type. + * It's used internally by Maven and can also be used by plugins and extensions to access + * custom enum values that have been registered through SPI providers. + * + * @param the specific type of extensible enum managed by this registry + * @since 4.0.0 + */ +public interface ExtensibleEnumRegistry extends Service { + @Nonnull + Optional lookup(@Nonnull String id); + + @Nonnull + default T require(@Nonnull String id) { + return lookup(id).orElseThrow(() -> new IllegalArgumentException("Unknown extensible enum value '" + id + "'")); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Interpolator.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Interpolator.java new file mode 100644 index 000000000000..790e938976c0 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Interpolator.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * The Interpolator service provides methods for variable substitution in strings and maps. + * It allows for the replacement of placeholders (e.g., ${variable}) with their corresponding values. + * + * @since 4.0.0 + */ +@Experimental +public interface Interpolator extends Service { + + /** + * Interpolates the values in the given map using the provided callback function. + * This method defaults to setting empty strings for unresolved placeholders. + * + * @param properties The map containing key-value pairs to be interpolated. + * @param callback The function to resolve variable values not found in the map. + */ + default void interpolate(@Nonnull Map properties, @Nullable UnaryOperator callback) { + interpolate(properties, callback, null, true); + } + + /** + * Interpolates the values in the given map using the provided callback function. + * + * @param map The map containing key-value pairs to be interpolated. + * @param callback The function to resolve variable values not found in the map. + * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings. If false, they are left unchanged. + */ + default void interpolate( + @Nonnull Map map, @Nullable UnaryOperator callback, boolean defaultsToEmpty) { + interpolate(map, callback, null, defaultsToEmpty); + } + + /** + * Interpolates the values in the given map using the provided callback function. + * + * @param map The map containing key-value pairs to be interpolated. + * @param callback The function to resolve variable values not found in the map. + * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings. If false, they are left unchanged. + */ + void interpolate( + @Nonnull Map map, + @Nullable UnaryOperator callback, + @Nullable BinaryOperator postprocessor, + boolean defaultsToEmpty); + + /** + * Interpolates a single string value using the provided callback function. + * This method defaults to not replacing unresolved placeholders. + * + * @param val The string to be interpolated. + * @param callback The function to resolve variable values. + * @return The interpolated string, or null if the input was null. + */ + @Nullable + default String interpolate(@Nullable String val, @Nullable UnaryOperator callback) { + return interpolate(val, callback, false); + } + + /** + * Interpolates a single string value using the provided callback function. + * + * @param val The string to be interpolated. + * @param callback The function to resolve variable values. + * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings. + * @return The interpolated string, or null if the input was null. + */ + @Nullable + default String interpolate( + @Nullable String val, @Nullable UnaryOperator callback, boolean defaultsToEmpty) { + return interpolate(val, callback, null, defaultsToEmpty); + } + + /** + * Interpolates a single string value using the provided callback function. + * + * @param val The string to be interpolated. + * @param callback The function to resolve variable values. + * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings. + * @return The interpolated string, or null if the input was null. + */ + @Nullable + String interpolate( + @Nullable String val, + @Nullable UnaryOperator callback, + @Nullable BinaryOperator postprocessor, + boolean defaultsToEmpty); + + /** + * Creates a composite function from a collection of functions. + * + * @param functions A collection of functions, each taking a String as input and returning a String. + * @return A function that applies each function in the collection in order until a non-null result is found. + * If all functions return null, the composite function returns null. + * + * @throws NullPointerException if the input collection is null or contains null elements. + */ + static UnaryOperator chain(Collection> functions) { + return s -> { + for (UnaryOperator function : functions) { + String v = function.apply(s); + if (v != null) { + return v; + } + } + return null; + }; + } + + @SafeVarargs + static UnaryOperator chain(UnaryOperator... functions) { + return chain(List.of(functions)); + } + + /** + * Memoizes a given function that takes a String input and produces a String output. + * This method creates a new function that caches the results of the original function, + * improving performance for repeated calls with the same input. + * + * @param callback The original function to be memoized. It takes a String as input and returns a String. + * @return A new {@code UnaryOperator} that caches the results of the original function. + * If the original function returns null for a given input, null will be cached and returned for subsequent calls with the same input. + * + * @see Function + * @see Optional + * @see HashMap#computeIfAbsent(Object, Function) + */ + static UnaryOperator memoize(UnaryOperator callback) { + Map> cache = new HashMap<>(); + return s -> cache.computeIfAbsent(s, v -> Optional.ofNullable(callback.apply(v))) + .orElse(null); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java new file mode 100644 index 000000000000..b7ca808a7879 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Exception thrown by {@link Interpolator} implementations when an error occurs during interpolation. + * This can include syntax errors in variable placeholders or recursive variable references. + * + * @since 4.0.0 + */ +@Experimental +public class InterpolatorException extends MavenException { + + @Serial + private static final long serialVersionUID = -1219149033636851813L; + + /** + * Constructs a new InterpolatorException with {@code null} as its + * detail message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause}. + */ + public InterpolatorException() {} + + /** + * Constructs a new InterpolatorException with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by + * a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public InterpolatorException(String message) { + super(message); + } + + /** + * Constructs a new InterpolatorException with the specified detail message and cause. + * + *

Note that the detail message associated with {@code cause} is not + * automatically incorporated in this exception's detail message.

+ * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). A {@code null} value is + * permitted, and indicates that the cause is nonexistent or unknown. + */ + public InterpolatorException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LanguageRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LanguageRegistry.java new file mode 100644 index 000000000000..d2f0f0933157 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LanguageRegistry.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Language; + +public interface LanguageRegistry extends ExtensibleEnumRegistry {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleRegistry.java new file mode 100644 index 000000000000..55efc16ebcd2 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleRegistry.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.apache.maven.api.Lifecycle; + +public interface LifecycleRegistry extends ExtensibleEnumRegistry, Iterable { + + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + List computePhases(Lifecycle lifecycle); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LocalRepositoryManager.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LocalRepositoryManager.java new file mode 100644 index 000000000000..70d7c9216f09 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LocalRepositoryManager.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.LocalRepository; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Manages the organization and access of artifacts within the local Maven repository. + * The local repository serves as a cache for downloaded remote artifacts and storage + * for locally installed artifacts. This manager provides services to determine the + * appropriate paths for artifacts within the local repository structure. + * + *

The LocalRepositoryManager is responsible for: + *

    + *
  • Determining the storage path for locally installed artifacts
  • + *
  • Managing the layout and organization of cached remote artifacts
  • + *
  • Maintaining consistency in artifact storage patterns
  • + *
+ * + *

This interface is part of Maven's repository management system and works in + * conjunction with {@link RemoteRepository} and {@link LocalRepository} to provide + * a complete artifact resolution and storage solution. + * + * @since 4.0.0 + * @see LocalRepository + * @see RemoteRepository + * @see Artifact + */ +@Experimental +public interface LocalRepositoryManager extends Service { + + /** + * Gets the relative path for a locally installed artifact. + * Note that the artifact need not actually exist yet at + * the returned location, the path merely indicates where + * the artifact would eventually be stored. + * + * @param session The session to use, must not be {@code null}. + * @param artifact The artifact for which to determine the path, must not be {@code null}. + * @return The path, resolved against the local repository's base directory. + */ + @Nonnull + Path getPathForLocalArtifact(@Nonnull Session session, @Nonnull LocalRepository local, @Nonnull Artifact artifact); + + /** + * Gets the relative path for an artifact cached from a remote repository. + * Note that the artifact need not actually exist yet at the returned location, + * the path merely indicates where the artifact would eventually be stored. + * + * @param session The session to use, must not be {@code null}. + * @param local The local repository, must not be {@code null}. + * @param artifact The artifact for which to determine the path, must not be {@code null}. + * @param remote – The source repository of the artifact, must not be {@code null}. + * @return The path, relative to the local repository's base directory. + */ + @Nonnull + Path getPathForRemoteArtifact( + @Nonnull Session session, + @Nonnull LocalRepository local, + @Nonnull RemoteRepository remote, + @Nonnull Artifact artifact); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Lookup.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Lookup.java new file mode 100644 index 000000000000..c80c2ed10e22 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Lookup.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Nonnull; + +public interface Lookup extends Service { + /** + * Performs a lookup for given typed component. + * + * @param type The component type. + * @return The component. + * @param The component type. + * @throws LookupException if no such component or there is some provisioning related issue. + */ + @Nonnull + T lookup(Class type); + + /** + * Performs a lookup for given typed component. + * + * @param type The component type. + * @param name The component name. + * @return The component. + * @param The component type. + * @throws LookupException if no such component or there is some provisioning related issue. + */ + @Nonnull + T lookup(Class type, String name); + + /** + * Performs a lookup for optional typed component. + * + * @param type The component type. + * @return Optional carrying component or empty optional if no such component. + * @param The component type. + * @throws LookupException if there is some provisioning related issue. + */ + @Nonnull + Optional lookupOptional(Class type); + + /** + * Performs a lookup for optional typed component. + * + * @param type The component type. + * @param name The component name. + * @return Optional carrying component or empty optional if no such component. + * @param The component type. + * @throws LookupException if there is some provisioning related issue. + */ + @Nonnull + Optional lookupOptional(Class type, String name); + + /** + * Performs a collection lookup for given typed components. + * + * @param type The component type. + * @return The list of components. The list may be empty if no components found. + * @param The component type. + * @throws LookupException if there is some provisioning related issue. + */ + @Nonnull + List lookupList(Class type); + + /** + * Performs a collection lookup for given typed components. + * + * @param type The component type. + * @return The map of components. The map may be empty if no components found. + * @param The component type. + * @throws LookupException if there is some provisioning related issue. + */ + @Nonnull + Map lookupMap(Class type); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java new file mode 100644 index 000000000000..97670213c15d --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link Lookup} service. + * + * @since 4.0.0 + */ +@Experimental +public class LookupException extends MavenException { + + @Serial + private static final long serialVersionUID = -6259322450070320286L; + + public LookupException(String message) { + super(message); + } + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public LookupException(String message, Exception e) { + super(message, e); + } + + /** + * @param e the {@link Exception} + */ + public LookupException(Exception e) { + super(e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java new file mode 100644 index 000000000000..a39cb9aa1a3e --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Base class for all maven exceptions carrying {@link BuilderProblem}s. + * + * @since 4.0.0 + */ +@Experimental +public abstract class MavenBuilderException extends MavenException { + + /** + * The collection of problems associated with this exception. + */ + private final ProblemCollector problems; + + /** + * Constructs a new exception with the specified message and cause. + * This constructor creates an empty problem collector. + * + * @param message the detail message + * @param cause the cause of this exception + */ + public MavenBuilderException(String message, Throwable cause) { + super(message, cause); + problems = ProblemCollector.empty(); + } + + /** + * Constructs a new exception with the specified message and problems. + * The message will be enhanced with details from the problems. + * + * @param message the detail message + * @param problems the collection of problems associated with this exception + */ + public MavenBuilderException(String message, ProblemCollector problems) { + super(buildMessage(message, problems), null); + this.problems = problems; + } + + /** + * Formats message out of problems: problems are sorted (in natural order of {@link BuilderProblem.Severity}) + * and then a list is built. These exceptions are usually thrown in "fatal" cases (and usually prevent Maven + * from starting), and these exceptions may end up very early on output. + * + * @param message the base message to enhance + * @param problems the collection of problems to include in the message + * @return a formatted message including details of all problems + */ + protected static String buildMessage(String message, ProblemCollector problems) { + StringBuilder msg = new StringBuilder(message); + problems.problems().forEach(problem -> msg.append("\n * ") + .append(problem.getSeverity().name()) + .append(": ") + .append(problem.getMessage())); + return msg.toString(); + } + + /** + * Returns the problem collector associated with this exception. + * + * @return the problem collector containing all problems related to this exception + */ + public ProblemCollector getProblemCollector() { + return problems; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java new file mode 100644 index 000000000000..5121ce8c7556 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * Base class for all maven exceptions. + * + * @since 4.0.0 + */ +@Experimental +public class MavenException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 9027638326336093132L; + + public MavenException() {} + + public MavenException(String message) { + super(message); + } + + public MavenException(String message, Throwable cause) { + super(message, cause); + } + + public MavenException(Throwable cause) { + super(cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java new file mode 100644 index 000000000000..ae0d249c334c --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java @@ -0,0 +1,274 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Constants; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Message builder that supports configurable styling. + * + * @since 4.0.0 + * @see MessageBuilderFactory + */ +public interface MessageBuilder extends Appendable { + + /** + * Append message content in trace style. + * By default, bold magenta + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder trace(Object message) { + return style("." + Constants.MAVEN_STYLE_TRACE_NAME + ":-" + Constants.MAVEN_STYLE_TRACE_DEFAULT, message); + } + + /** + * Append message content in debug style. + * By default, bold cyan + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder debug(Object message) { + return style("." + Constants.MAVEN_STYLE_DEBUG_NAME + ":-" + Constants.MAVEN_STYLE_DEBUG_DEFAULT, message); + } + + /** + * Append message content in info style. + * By default, bold blue + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder info(Object message) { + return style("." + Constants.MAVEN_STYLE_INFO_NAME + ":-" + Constants.MAVEN_STYLE_INFO_DEFAULT, message); + } + + /** + * Append message content in warning style. + * By default, bold yellow + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder warning(Object message) { + return style("." + Constants.MAVEN_STYLE_WARNING_NAME + ":-" + Constants.MAVEN_STYLE_WARNING_DEFAULT, message); + } + + /** + * Append message content in error style. + * By default, bold red + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder error(Object message) { + return style("." + Constants.MAVEN_STYLE_ERROR_NAME + ":-" + Constants.MAVEN_STYLE_ERROR_DEFAULT, message); + } + + /** + * Append message content in success style. + * By default, bold green + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder success(Object message) { + return style("." + Constants.MAVEN_STYLE_SUCCESS_NAME + ":-" + Constants.MAVEN_STYLE_SUCCESS_DEFAULT, message); + } + + /** + * Append message content in failure style. + * By default, bold red + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder failure(Object message) { + return style("." + Constants.MAVEN_STYLE_FAILURE_NAME + ":-" + Constants.MAVEN_STYLE_FAILURE_DEFAULT, message); + } + + /** + * Append message content in strong style. + * By default, bold + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder strong(Object message) { + return style("." + Constants.MAVEN_STYLE_STRONG_NAME + ":-" + Constants.MAVEN_STYLE_STRONG_DEFAULT, message); + } + + /** + * Append message content in mojo style. + * By default, green + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder mojo(Object message) { + return style("." + Constants.MAVEN_STYLE_MOJO_NAME + ":-" + Constants.MAVEN_STYLE_MOJO_DEFAULT, message); + } + + /** + * Append message content in project style. + * By default, cyan + * + * @param message the message to append + * @return the current builder + */ + @Nonnull + default MessageBuilder project(Object message) { + return style("." + Constants.MAVEN_STYLE_PROJECT_NAME + ":-" + Constants.MAVEN_STYLE_PROJECT_DEFAULT, message); + } + + @Nonnull + default MessageBuilder style(String style, Object message) { + return style(style).a(message).resetStyle(); + } + + MessageBuilder style(String style); + + MessageBuilder resetStyle(); + + // + // message building methods modelled after Ansi methods + // + + @Nonnull + @Override + MessageBuilder append(CharSequence cs); + + @Nonnull + @Override + MessageBuilder append(CharSequence cs, int start, int end); + + @Nonnull + @Override + MessageBuilder append(char c); + + /** + * Append content to the message buffer. + * + * @param value the content to append + * @param offset the index of the first {@code char} to append + * @param len the number of {@code char}s to append + * @return the current builder + */ + @Nonnull + default MessageBuilder a(char[] value, int offset, int len) { + return append(String.valueOf(value, offset, len)); + } + + /** + * Append content to the message buffer. + * + * @param value the content to append + * @return the current builder + */ + @Nonnull + default MessageBuilder a(char[] value) { + return append(String.valueOf(value)); + } + + /** + * Append content to the message buffer. + * + * @param value the content to append + * @param start the starting index of the subsequence to be appended + * @param end the end index of the subsequence to be appended + * @return the current builder + */ + @Nonnull + default MessageBuilder a(CharSequence value, int start, int end) { + return append(value, start, end); + } + + /** + * Append content to the message buffer. + * + * @param value the content to append + * @return the current builder + */ + @Nonnull + default MessageBuilder a(CharSequence value) { + return append(value); + } + + /** + * Append content to the message buffer. + * + * @param value the content to append + * @return the current builder + */ + @Nonnull + default MessageBuilder a(Object value) { + return append(String.valueOf(value)); + } + + /** + * Append newline to the message buffer. + * + * @return the current builder + */ + @Nonnull + default MessageBuilder newline() { + return append(System.lineSeparator()); + } + + /** + * Append formatted content to the buffer. + * @see String#format(String, Object...) + * + * @param pattern a format string + * @param args arguments referenced by the format specifiers in the format string + * @return the current builder + */ + @Nonnull + default MessageBuilder format(String pattern, Object... args) { + return append(String.format(pattern, args)); + } + + /** + * Set the buffer length. + * + * @param length the new length + * @return the current builder + */ + MessageBuilder setLength(int length); + + /** + * Return the built message. + * + * @return the message + */ + @Nonnull + String build(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java new file mode 100644 index 000000000000..9a595ae11095 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * A factory for {@link MessageBuilder}. + * + * @since 4.0.0 + */ +@Experimental +public interface MessageBuilderFactory extends Service { + /** + * Checks if the underlying output does support styling or not. + * @return whether color styling is supported or not + */ + boolean isColorEnabled(); + + /** + * Returns the terminal width or -1 if not supported. + * @return the terminal width + */ + int getTerminalWidth(); + + /** + * Creates a new message builder. + * @return a new message builder + */ + @Nonnull + MessageBuilder builder(); + + /** + * Creates a new message builder of the specified size. + * @param size the initial size of the message builder buffer + * @return a new message builder + */ + @Nonnull + MessageBuilder builder(int size); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java new file mode 100644 index 000000000000..ea8e263392ed --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; + +import org.apache.maven.api.Service; +import org.apache.maven.api.model.Model; + +public interface ModelBuilder extends Service { + + String MODEL_VERSION_4_0_0 = "4.0.0"; + + String MODEL_VERSION_4_1_0 = "4.1.0"; + + String MODEL_VERSION_4_2_0 = "4.2.0"; + + List KNOWN_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0, MODEL_VERSION_4_2_0); + + ModelBuilderSession newSession(); + + interface ModelBuilderSession { + + ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException; + } + + Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java new file mode 100644 index 000000000000..c01fa138ab9d --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link ProjectBuilder} service. + * + * @since 4.0.0 + */ +@Experimental +public class ModelBuilderException extends MavenException { + + @Serial + private static final long serialVersionUID = -1865447022070650896L; + + private final ModelBuilderResult result; + + /** + * Creates a new exception from the specified interim result and its associated problems. + * + * @param result The interim result, may be {@code null}. + */ + public ModelBuilderException(ModelBuilderResult result) { + super(result.toString()); + this.result = result; + } + + /** + * Gets the interim result of the model building up to the point where it failed. + * + * @return The interim model building result or {@code null} if not available. + */ + public ModelBuilderResult getResult() { + return result; + } + + /** + * Gets the identifier of the POM whose effective model could not be built. The general format of the identifier is + * {@code ::} but some of these coordinates may still be unknown at the point the + * exception is thrown so this information is merely meant to assist the user. + * + * @return The identifier of the POM or an empty string if not known, never {@code null}. + */ + public String getModelId() { + if (result == null) { + return ""; + } else if (result.getEffectiveModel() != null) { + return result.getEffectiveModel().getId(); + } else if (result.getRawModel() != null) { + return result.getRawModel().getId(); + } else if (result.getFileModel() != null) { + return result.getFileModel().getId(); + } else { + return ""; + } + } + + /** + * Gets the problems that caused this exception. + * + * @return The problems that caused this exception, never {@code null}. + */ + public ProblemCollector getProblemCollector() { + if (result == null) { + return ProblemCollector.empty(); + } + return result.getProblemCollector(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java new file mode 100644 index 000000000000..14141a6d0c6c --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java @@ -0,0 +1,458 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.model.Profile; + +import static java.util.Objects.requireNonNull; + +/** + * Request used to build a {@link org.apache.maven.api.Project} using + * the {@link ProjectBuilder} service. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ModelBuilderRequest extends Request { + + /** + * The possible request types for building a model. + */ + enum RequestType { + /** + * The request is for building an initial model from a POM file in a project on the filesystem. + */ + BUILD_PROJECT, + /** + * The request is for rebuilding the effective POM in a project on the filesystem. + */ + BUILD_EFFECTIVE, + /** + * The request is used specifically to parse the POM used as a basis for creating the consumer POM. + * This POM will not undergo any profile activation. + */ + BUILD_CONSUMER, + /** + * The request is for building a model from a parent POM file from a downloaded artifact. + */ + CONSUMER_PARENT, + /** + * The request is for building a model from a dependency POM file from a downloaded artifact. + */ + CONSUMER_DEPENDENCY + } + + /** + * The possible merge modes for combining remote repositories. + */ + enum RepositoryMerging { + + /** + * The repositories declared in the POM have precedence over the repositories specified in the request. + */ + POM_DOMINANT, + + /** + * The repositories specified in the request have precedence over the repositories declared in the POM. + */ + REQUEST_DOMINANT, + } + + @Nonnull + ModelSource getSource(); + + @Nonnull + RequestType getRequestType(); + + boolean isLocationTracking(); + + boolean isRecursive(); + + /** + * Defines external profiles that may be activated for the given model. + * Those are external profiles usually defined in {@link org.apache.maven.api.settings.Settings#getProfiles()}. + */ + @Nonnull + Collection getProfiles(); + + /** + * List of profile ids that have been explicitly activated by the user. + */ + @Nonnull + List getActiveProfileIds(); + + /** + * List of profile ids that have been explicitly deactivated by the user. + */ + @Nonnull + List getInactiveProfileIds(); + + /** + * Provides a map of system properties. + */ + @Nonnull + Map getSystemProperties(); + + /** + * Provides a map of user properties. + * User properties + */ + @Nonnull + Map getUserProperties(); + + @Nonnull + RepositoryMerging getRepositoryMerging(); + + @Nullable + List getRepositories(); + + @Nullable + ModelTransformer getLifecycleBindingsInjector(); + + @Nonnull + static ModelBuilderRequest build(@Nonnull ModelBuilderRequest request, @Nonnull ModelSource source) { + return builder(requireNonNull(request, "request cannot be null")) + .source(requireNonNull(source, "source cannot be null")) + .build(); + } + + @Nonnull + static ModelBuilderRequest build(@Nonnull Session session, @Nonnull ModelSource source) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .source(requireNonNull(source, "source cannot be null")) + .build(); + } + + @Nonnull + static ModelBuilderRequest build(@Nonnull Session session, @Nonnull Path path) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .source(Sources.buildSource(path)) + .build(); + } + + @Nonnull + static ModelBuilderRequestBuilder builder() { + return new ModelBuilderRequestBuilder(); + } + + @Nonnull + static ModelBuilderRequestBuilder builder(ModelBuilderRequest request) { + return new ModelBuilderRequestBuilder(request); + } + + @NotThreadSafe + class ModelBuilderRequestBuilder { + Session session; + RequestTrace trace; + RequestType requestType; + boolean locationTracking; + boolean recursive; + ModelSource source; + Collection profiles; + List activeProfileIds; + List inactiveProfileIds; + Map systemProperties; + Map userProperties; + RepositoryMerging repositoryMerging; + List repositories; + ModelTransformer lifecycleBindingsInjector; + + ModelBuilderRequestBuilder() {} + + ModelBuilderRequestBuilder(ModelBuilderRequest request) { + this.session = request.getSession(); + this.trace = request.getTrace(); + this.requestType = request.getRequestType(); + this.locationTracking = request.isLocationTracking(); + this.recursive = request.isRecursive(); + this.source = request.getSource(); + this.profiles = request.getProfiles(); + this.activeProfileIds = request.getActiveProfileIds(); + this.inactiveProfileIds = request.getInactiveProfileIds(); + this.systemProperties = request.getSystemProperties(); + this.userProperties = request.getUserProperties(); + this.repositoryMerging = request.getRepositoryMerging(); + this.repositories = request.getRepositories(); + this.lifecycleBindingsInjector = request.getLifecycleBindingsInjector(); + } + + public ModelBuilderRequestBuilder session(Session session) { + this.session = session; + return this; + } + + public ModelBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public ModelBuilderRequestBuilder requestType(RequestType requestType) { + this.requestType = requestType; + return this; + } + + public ModelBuilderRequestBuilder locationTracking(boolean locationTracking) { + this.locationTracking = locationTracking; + return this; + } + + public ModelBuilderRequestBuilder recursive(boolean recursive) { + this.recursive = recursive; + return this; + } + + public ModelBuilderRequestBuilder source(ModelSource source) { + this.source = source; + return this; + } + + public ModelBuilderRequestBuilder profiles(List profiles) { + this.profiles = profiles; + return this; + } + + public ModelBuilderRequestBuilder activeProfileIds(List activeProfileIds) { + this.activeProfileIds = activeProfileIds; + return this; + } + + public ModelBuilderRequestBuilder inactiveProfileIds(List inactiveProfileIds) { + this.inactiveProfileIds = inactiveProfileIds; + return this; + } + + public ModelBuilderRequestBuilder systemProperties(Map systemProperties) { + this.systemProperties = systemProperties; + return this; + } + + public ModelBuilderRequestBuilder userProperties(Map userProperties) { + this.userProperties = userProperties; + return this; + } + + public ModelBuilderRequestBuilder repositoryMerging(RepositoryMerging repositoryMerging) { + this.repositoryMerging = repositoryMerging; + return this; + } + + public ModelBuilderRequestBuilder repositories(List repositories) { + this.repositories = repositories; + return this; + } + + public ModelBuilderRequestBuilder lifecycleBindingsInjector(ModelTransformer lifecycleBindingsInjector) { + this.lifecycleBindingsInjector = lifecycleBindingsInjector; + return this; + } + + public ModelBuilderRequest build() { + return new DefaultModelBuilderRequest( + session, + trace, + requestType, + locationTracking, + recursive, + source, + profiles, + activeProfileIds, + inactiveProfileIds, + systemProperties, + userProperties, + repositoryMerging, + repositories, + lifecycleBindingsInjector); + } + + private static class DefaultModelBuilderRequest extends BaseRequest implements ModelBuilderRequest { + private final RequestType requestType; + private final boolean locationTracking; + private final boolean recursive; + private final ModelSource source; + private final Collection profiles; + private final List activeProfileIds; + private final List inactiveProfileIds; + private final Map systemProperties; + private final Map userProperties; + private final RepositoryMerging repositoryMerging; + private final List repositories; + private final ModelTransformer lifecycleBindingsInjector; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultModelBuilderRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull RequestType requestType, + boolean locationTracking, + boolean recursive, + @Nonnull ModelSource source, + Collection profiles, + List activeProfileIds, + List inactiveProfileIds, + Map systemProperties, + Map userProperties, + RepositoryMerging repositoryMerging, + List repositories, + ModelTransformer lifecycleBindingsInjector) { + super(session, trace); + this.requestType = requireNonNull(requestType, "requestType cannot be null"); + this.locationTracking = locationTracking; + this.recursive = recursive; + this.source = source; + this.profiles = profiles != null ? List.copyOf(profiles) : List.of(); + this.activeProfileIds = activeProfileIds != null ? List.copyOf(activeProfileIds) : List.of(); + this.inactiveProfileIds = inactiveProfileIds != null ? List.copyOf(inactiveProfileIds) : List.of(); + this.systemProperties = + systemProperties != null ? Map.copyOf(systemProperties) : session.getSystemProperties(); + this.userProperties = userProperties != null ? Map.copyOf(userProperties) : session.getUserProperties(); + this.repositoryMerging = repositoryMerging; + this.repositories = repositories != null ? List.copyOf(repositories) : null; + this.lifecycleBindingsInjector = lifecycleBindingsInjector; + } + + @Override + public RequestType getRequestType() { + return requestType; + } + + @Override + public boolean isLocationTracking() { + return locationTracking; + } + + @Override + public boolean isRecursive() { + return recursive; + } + + @Nonnull + @Override + public ModelSource getSource() { + return source; + } + + @Override + public Collection getProfiles() { + return profiles; + } + + @Override + public List getActiveProfileIds() { + return activeProfileIds; + } + + @Override + public List getInactiveProfileIds() { + return inactiveProfileIds; + } + + @Override + public Map getSystemProperties() { + return systemProperties; + } + + @Override + public Map getUserProperties() { + return userProperties; + } + + @Override + public RepositoryMerging getRepositoryMerging() { + return repositoryMerging; + } + + @Override + public List getRepositories() { + return repositories; + } + + @Override + public ModelTransformer getLifecycleBindingsInjector() { + return lifecycleBindingsInjector; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultModelBuilderRequest that + && locationTracking == that.locationTracking + && recursive == that.recursive + && requestType == that.requestType + && Objects.equals(source, that.source) + && Objects.equals(profiles, that.profiles) + && Objects.equals(activeProfileIds, that.activeProfileIds) + && Objects.equals(inactiveProfileIds, that.inactiveProfileIds) + && Objects.equals(systemProperties, that.systemProperties) + && Objects.equals(userProperties, that.userProperties) + && repositoryMerging == that.repositoryMerging + && Objects.equals(repositories, that.repositories) + && Objects.equals(lifecycleBindingsInjector, that.lifecycleBindingsInjector); + } + + @Override + public int hashCode() { + return Objects.hash( + requestType, + locationTracking, + recursive, + source, + profiles, + activeProfileIds, + inactiveProfileIds, + systemProperties, + userProperties, + repositoryMerging, + repositories, + lifecycleBindingsInjector); + } + + @Override + public String toString() { + return "ModelBuilderRequest[" + "requestType=" + + requestType + ", locationTracking=" + + locationTracking + ", recursive=" + + recursive + ", source=" + + source + ", profiles=" + + profiles + ", activeProfileIds=" + + activeProfileIds + ", inactiveProfileIds=" + + inactiveProfileIds + ", systemProperties=" + + systemProperties + ", userProperties=" + + userProperties + ", repositoryMerging=" + + repositoryMerging + ", repositories=" + + repositories + ", lifecycleBindingsInjector=" + + lifecycleBindingsInjector + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java new file mode 100644 index 000000000000..4b15818cf033 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.model.Profile; + +/** + * Result of a project build call. + * + * @since 4.0.0 + */ +@Experimental +public interface ModelBuilderResult extends Result { + + /** + * Gets the source from which the model was read. + * + * @return The source from which the model was read, never {@code null}. + */ + @Nonnull + ModelSource getSource(); + + /** + * Gets the file model. + * + * @return the file model, never {@code null}. + */ + @Nonnull + Model getFileModel(); + + /** + * Gets the file model + build pom transformation, without inheritance nor interpolation. + * + * @return The raw model, never {@code null}. + */ + @Nonnull + Model getRawModel(); + + /** + * Gets the effective model of the parent POM. + * + * @return the effective model of the parent POM, never {@code null} + */ + @Nonnull + Model getParentModel(); + + /** + * Gets the assembled model with inheritance, interpolation and profile injection. + * + * @return The assembled model, never {@code null}. + */ + @Nonnull + Model getEffectiveModel(); + + /** + * Gets the profiles that were active during model building. + * + * @return The active profiles of the model or an empty list if the model has no active profiles. + */ + @Nonnull + List getActivePomProfiles(); + + /** + * Gets the external profiles that were active during model building. External profiles are those that were + * contributed by {@link ModelBuilderRequest#getProfiles()}. + * + * @return The active external profiles or an empty list if none, never {@code null}. + */ + @Nonnull + List getActiveExternalProfiles(); + + /** + * Gets the problem collector that collected problems encountered during the project building. + * + * @return the problem collector that collected problems encountered during the project building + */ + @Nonnull + ProblemCollector getProblemCollector(); + + /** + * Gets the children of this result. + * + * @return the children of this result, can be empty but never {@code null} + */ + @Nonnull + List getChildren(); + + /** + * Creates a human-readable representation of these errors. + */ + @Override + String toString(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java new file mode 100644 index 000000000000..63bad258bf1d --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Nonnull; + +/** + * Describes a problem that was encountered during model building. A problem can either be an exception that was thrown + * or a simple string message. In addition, a problem carries a hint about its source, e.g. the POM file that exhibits + * the problem. + * + */ +public interface ModelProblem extends BuilderProblem { + + /** + * Enumeration of model versions that can be validated. + * These versions correspond to different levels of validation that can be applied + * during model building, based on the POM schema version. + *

+ * The validation levels are cumulative, with higher versions including all validations + * from lower versions plus additional checks specific to that version. + */ + enum Version { + /** + * Base validation level that applies to all POM versions. + * Includes fundamental structural validations. + */ + BASE, + + /** + * Validation for Maven 2.0 POM format. + */ + V20, + + /** + * Validation for Maven 3.0 POM format. + */ + V30, + + /** + * Validation for Maven 3.1 POM format. + */ + V31, + + /** + * Validation for Maven 4.0 POM format. + */ + V40, + + /** + * Validation for Maven 4.1 POM format. + */ + V41, + + /** + * Validation for Maven 4.2 POM format. + */ + V42 + } + + /** + * Gets the identifier of the model from which the problem originated. The identifier is derived from the + * information that is available at the point the problem occurs and as such merely serves as best effort + * to provide information to the user to track the problem back to its origin. + * + * @return The identifier of the model from which the problem originated or an empty string if unknown, never + * {@code null}. + */ + @Nonnull + String getModelId(); + + /** + * Gets the applicable maven version/validation level of this problem + * @return The version, never {@code null}. + */ + @Nonnull + Version getVersion(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java new file mode 100644 index 000000000000..66c1fcc31ead --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.model.InputLocation; +import org.apache.maven.api.model.Model; + +/** + * Collects problems that are encountered during model building. The primary purpose of this component is to account for + * the fact that the problem reporter has/should not have information about the calling context and hence cannot provide + * an expressive source hint for the model problem. Instead, the source hint is configured by the model builder before + * it delegates to other components that potentially encounter problems. Then, the problem reporter can focus on + * providing a simple error message, leaving the donkey work of creating a nice model problem to this component. + * + */ +public interface ModelProblemCollector { + + ProblemCollector getProblemCollector(); + + default boolean hasErrors() { + return getProblemCollector().hasErrorProblems(); + } + + default boolean hasFatalErrors() { + return getProblemCollector().hasFatalProblems(); + } + + default void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) { + add(severity, version, message, null, null); + } + + default void add( + BuilderProblem.Severity severity, ModelProblem.Version version, String message, InputLocation location) { + add(severity, version, message, location, null); + } + + default void add( + BuilderProblem.Severity severity, ModelProblem.Version version, String message, Exception exception) { + add(severity, version, message, null, exception); + } + + void add( + BuilderProblem.Severity severity, + ModelProblem.Version version, + String message, + InputLocation location, + Exception exception); + + default void add(ModelProblem problem) { + getProblemCollector().reportProblem(problem); + } + + ModelBuilderException newModelBuilderException(); + + void setSource(String location); + + void setSource(Model model); + + String getSource(); + + void setRootModel(Model model); + + Model getRootModel(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java new file mode 100644 index 000000000000..325b1d4419d1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; + +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Represents a source for loading Maven Project Object Model (POM) files. This interface + * extends the basic {@link Source} interface with specific functionality for handling + * Maven POM files and resolving related project POMs. + * + *

The interface provides two types of sources:

+ *
    + *
  • Build sources: Used for POM files of projects being built by Maven in the filesystem. + * These sources support resolving related POMs using the {@link ModelLocator}.
  • + *
  • Resolved sources: Used for artifacts that have been resolved by Maven from repositories + * (using groupId:artifactId:version coordinates) and downloaded to the local repository. + * These sources do not support resolving other sources.
  • + *
+ * + * @since 4.0.0 + * @see Source + */ +public interface ModelSource extends Source { + + /** + * Interface for locating POM files within a project structure. + * Implementations of this interface provide the ability to find POM files + * in various project contexts. + * + * @since 4.0.0 + */ + interface ModelLocator { + /** + * Attempts to locate an existing POM file at or within the specified project path. + * + *

This method is used to find POM files in various contexts, such as:

+ *
    + *
  • Directly at the specified path
  • + *
  • Within a directory at the specified path
  • + *
  • In standard Maven project locations relative to the specified path
  • + *
+ * + * @param project the path to search for a POM file + * @return the path to the located POM file, or null if no POM can be found + * @throws NullPointerException if project is null + */ + @Nullable + Path locateExistingPom(@Nonnull Path project); + } + + /** + * Resolves a relative path to another POM file using the provided model locator. + * This method is specifically used to locate POM files for subprojects or related + * projects referenced from the current POM. + * + *

The resolution process typically involves:

+ *
    + *
  • Normalizing the relative path for the current platform
  • + *
  • Resolving the path against the current POM's location
  • + *
  • Using the model locator to find an existing POM at the resolved location
  • + *
+ * + * @param modelLocator the locator to use for finding the related POM file + * @param relative the relative path to resolve + * @return a new ModelSource for the resolved POM, or null if: + *
    + *
  • This is not a build source
  • + *
  • No POM can be found at the resolved location
  • + *
+ * @throws NullPointerException if modelLocator or relative is null + */ + @Nullable + ModelSource resolve(@Nonnull ModelLocator modelLocator, @Nonnull String relative); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java new file mode 100644 index 000000000000..5d2503ec31bc --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Model; + +/** + * A model transformer. + * + * @since 4.0.0 + */ +@Experimental +public interface ModelTransformer { + + /** + * Apply a transformation on the file model. + * + * @param model the input model + * @param problems the problem collector to report any issues encountered during transformation + * @return the transformed model, or the input model if no transformation is needed + */ + @Nonnull + Model transform( + @Nonnull Model model, @Nonnull ModelBuilderRequest request, @Nonnull ModelProblemCollector problems); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/OsService.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/OsService.java new file mode 100644 index 000000000000..a07b1eabd981 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/OsService.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Service for detecting and providing information about the operating system (OS) + * on which the application is running. + *

+ * This service provides a platform-independent way to: + *

    + *
  • Query basic OS information like name, architecture, and version
  • + *
  • Determine the OS family (e.g., Windows, Unix, Mac)
  • + *
  • Check if the current OS is Windows-based
  • + *
+ *

+ * The service implementation uses system properties to detect OS characteristics: + *

    + *
  • os.name: The operating system name
  • + *
  • os.arch: The operating system architecture
  • + *
  • os.version: The operating system version
  • + *
+ *

+ * Supported OS families include: + *

    + *
  • windows: All Windows variants
  • + *
  • win9x: Windows 95, 98, ME, CE
  • + *
  • winnt: Windows NT variants
  • + *
  • unix: Unix-like systems (including Linux)
  • + *
  • mac: macOS (including Darwin)
  • + *
  • os/2: OS/2 variants
  • + *
  • netware: Novell NetWare
  • + *
  • dos: DOS variants
  • + *
  • tandem: Tandem systems
  • + *
  • openvms: OpenVMS
  • + *
  • z/os: z/OS and OS/390
  • + *
  • os/400: OS/400
  • + *
+ * + * @since 4.0.0 + */ +@Experimental +public interface OsService extends Service { + /** + * Returns the OS full name as reported by the system property "os.name". + * The value is converted to lowercase for consistency. + * + * @return the operating system name (never null) + */ + @Nonnull + String name(); + + /** + * Returns the OS architecture as reported by the system property "os.arch". + * The value is converted to lowercase for consistency. + * + * @return the operating system architecture (never null) + */ + @Nonnull + String arch(); + + /** + * Returns the OS version as reported by the system property "os.version". + * The value is converted to lowercase for consistency. + * + * @return the operating system version (never null) + */ + @Nonnull + String version(); + + /** + * Returns the OS family name based on OS detection rules. + * This categorizes the OS into one of the supported families + * (e.g., "windows", "unix", "mac"). + * + * @return the operating system family name (never null) + */ + @Nonnull + String family(); + + /** + * Checks if the current operating system belongs to the Windows family. + * This includes all Windows variants (95, 98, ME, NT, 2000, XP, Vista, 7, 8, 10, 11). + * + * @return true if the current OS is any Windows variant, false otherwise + */ + boolean isWindows(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PackagingRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PackagingRegistry.java new file mode 100644 index 000000000000..e114e2664058 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PackagingRegistry.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Packaging; + +public interface PackagingRegistry extends ExtensibleEnumRegistry {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathMatcherFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathMatcherFactory.java new file mode 100644 index 000000000000..9f83e2e0f8bf --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathMatcherFactory.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.Collection; +import java.util.Objects; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Service for creating {@link PathMatcher} objects that can be used to filter files + * based on include/exclude patterns. This service provides a clean API for plugins + * to create path matchers without directly depending on implementation classes. + *

+ * The path matchers created by this service support Maven's traditional include/exclude + * pattern syntax, which is compatible with the behavior of Maven 3 plugins like + * maven-compiler-plugin and maven-clean-plugin. + *

+ * Pattern syntax supports: + *

    + *
  • Standard glob patterns with {@code *}, {@code ?}, and {@code **} wildcards
  • + *
  • Explicit syntax prefixes like {@code "glob:"} or {@code "regex:"}
  • + *
  • Maven 3 compatible behavior for patterns without explicit syntax
  • + *
  • Default exclusion patterns for SCM files when requested
  • + *
+ * + * @since 4.0.0 + * @see PathMatcher + */ +@Experimental +public interface PathMatcherFactory extends Service { + + /** + * Creates a path matcher for filtering files based on include and exclude patterns. + *

+ * The pathnames used for matching will be relative to the specified base directory + * and use {@code '/'} as separator, regardless of the hosting operating system. + * + * @param baseDirectory the base directory for relativizing paths during matching + * @param includes the patterns of files to include, or null/empty for including all files + * @param excludes the patterns of files to exclude, or null/empty for no exclusion + * @param useDefaultExcludes whether to augment excludes with default SCM exclusion patterns + * @return a PathMatcher that can be used to test if paths should be included + * @throws NullPointerException if baseDirectory is null + */ + @Nonnull + PathMatcher createPathMatcher( + @Nonnull Path baseDirectory, + Collection includes, + Collection excludes, + boolean useDefaultExcludes); + + /** + * Creates a path matcher for filtering files based on include and exclude patterns, + * without using default exclusion patterns. + *

+ * This is equivalent to calling {@link #createPathMatcher(Path, Collection, Collection, boolean)} + * with {@code useDefaultExcludes = false}. + * + * @param baseDirectory the base directory for relativizing paths during matching + * @param includes the patterns of files to include, or null/empty for including all files + * @param excludes the patterns of files to exclude, or null/empty for no exclusion + * @return a PathMatcher that can be used to test if paths should be included + * @throws NullPointerException if baseDirectory is null + */ + @Nonnull + default PathMatcher createPathMatcher( + @Nonnull Path baseDirectory, Collection includes, Collection excludes) { + return createPathMatcher(baseDirectory, includes, excludes, false); + } + + /** + * Creates a path matcher that includes all files except those matching the exclude patterns. + *

+ * This is equivalent to calling {@link #createPathMatcher(Path, Collection, Collection, boolean)} + * with {@code includes = null}. + * + * @param baseDirectory the base directory for relativizing paths during matching + * @param excludes the patterns of files to exclude, or null/empty for no exclusion + * @param useDefaultExcludes whether to augment excludes with default SCM exclusion patterns + * @return a PathMatcher that can be used to test if paths should be included + * @throws NullPointerException if baseDirectory is null + */ + @Nonnull + default PathMatcher createExcludeOnlyMatcher( + @Nonnull Path baseDirectory, Collection excludes, boolean useDefaultExcludes) { + return createPathMatcher(baseDirectory, null, excludes, useDefaultExcludes); + } + + /** + * Creates a path matcher that only includes files matching the include patterns. + *

+ * This is equivalent to calling {@link #createPathMatcher(Path, Collection, Collection, boolean)} + * with {@code excludes = null} and {@code useDefaultExcludes = false}. + * + * @param baseDirectory the base directory for relativizing paths during matching + * @param includes the patterns of files to include, or null/empty for including all files + * @return a PathMatcher that can be used to test if paths should be included + * @throws NullPointerException if baseDirectory is null + */ + @Nonnull + default PathMatcher createIncludeOnlyMatcher(@Nonnull Path baseDirectory, Collection includes) { + return createPathMatcher(baseDirectory, includes, null, false); + } + + /** + * Returns a filter for directories that may contain paths accepted by the given matcher. + * The given path matcher should be an instance created by this service. + * The path matcher returned by this method expects directory paths. + * If that matcher returns {@code false}, then the directory will definitively not contain + * the paths selected by the matcher given in argument to this method. + * In such case, the whole directory and all its sub-directories can be skipped. + * In case of doubt, or if the matcher given in argument is not recognized by this method, + * then the matcher returned by this method will return {@code true}. + * + * @param fileMatcher a matcher created by one of the other methods of this interface + * @return filter for directories that may contain the selected files + * @throws NullPointerException if fileMatcher is null + */ + @Nonnull + PathMatcher deriveDirectoryMatcher(@Nonnull PathMatcher fileMatcher); + + /** + * Returns the path matcher that unconditionally returns {@code true} for all files. + * It should be the matcher returned by the other methods of this interface when the + * given patterns match all files. + * + * @return path matcher that unconditionally returns {@code true} for all files + */ + @Nonnull + PathMatcher includesAll(); + + /** + * {@return whether the given matcher includes all files}. + * This method may conservatively returns {@code false} if case of doubt. + * A return value of {@code true} means that the pattern is certain to match all files. + * + * @param matcher the matcher to test + */ + default boolean isIncludesAll(@Nonnull PathMatcher matcher) { + return Objects.requireNonNull(matcher) == includesAll(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathScopeRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathScopeRegistry.java new file mode 100644 index 000000000000..06b5d5964733 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathScopeRegistry.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.PathScope; + +public interface PathScopeRegistry extends ExtensibleEnumRegistry {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java new file mode 100644 index 000000000000..11fbb4bce04b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java @@ -0,0 +1,309 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.apache.maven.api.Constants; +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * Collects problems that were encountered during project building. + * + * @param

The type of the problem. + * @since 4.0.0 + */ +@Experimental +public interface ProblemCollector

{ + /** + * Returns {@code true} if there is at least one problem collected with severity equal or more severe than + * {@link org.apache.maven.api.services.BuilderProblem.Severity#WARNING}. This check is logically equivalent + * to "is there any problem reported?", given warning is the lowest severity. + */ + default boolean hasWarningProblems() { + return hasProblemsFor(BuilderProblem.Severity.WARNING); + } + + /** + * Returns {@code true} if there is at least one problem collected with severity equal or more severe than + * {@link org.apache.maven.api.services.BuilderProblem.Severity#ERROR}. + */ + default boolean hasErrorProblems() { + return hasProblemsFor(BuilderProblem.Severity.ERROR); + } + + /** + * Returns {@code true} if there is at least one problem collected with severity equal or more severe than + * {@link org.apache.maven.api.services.BuilderProblem.Severity#FATAL}. + */ + default boolean hasFatalProblems() { + return hasProblemsFor(BuilderProblem.Severity.FATAL); + } + + /** + * Returns {@code true} if there is at least one problem collected with severity equal or more severe than + * passed in severity. + */ + default boolean hasProblemsFor(BuilderProblem.Severity severity) { + requireNonNull(severity, "severity"); + for (BuilderProblem.Severity s : BuilderProblem.Severity.values()) { + if (s.ordinal() <= severity.ordinal() && problemsReportedFor(s) > 0) { + return true; + } + } + return false; + } + + /** + * Returns total count of problems reported. + */ + default int totalProblemsReported() { + return problemsReportedFor(BuilderProblem.Severity.values()); + } + + /** + * Returns count of problems reported for given severities. + * + * @param severities the severity levels to count problems for + * @return the total count of problems for the specified severities + */ + int problemsReportedFor(BuilderProblem.Severity... severities); + + /** + * Returns {@code true} if reported problem count exceeded allowed count, and issues were lost. When this + * method returns {@code true}, it means that element count of stream returned by method {@link #problems()} + * and the counter returned by {@link #totalProblemsReported()} are not equal (latter is bigger than former). + * + * @return true if the problem collector has overflowed and some problems were not preserved + */ + boolean problemsOverflow(); + + /** + * Reports a problem: always maintains the counters, but whether problem is preserved in memory, depends on + * implementation and its configuration. + * + * @param problem the problem to report + * @return {@code true} if passed problem is preserved by this call. + */ + boolean reportProblem(P problem); + + /** + * Returns all reported and preserved problems ordered by severity in decreasing order. Note: counters and + * element count in this stream does not have to be equal. + */ + @Nonnull + default Stream

problems() { + Stream

result = Stream.empty(); + for (BuilderProblem.Severity severity : BuilderProblem.Severity.values()) { + result = Stream.concat(result, problems(severity)); + } + return result; + } + + /** + * Returns all reported and preserved problems for given severity. Note: counters and element count in this + * stream does not have to be equal. + * + * @param severity the severity level to get problems for + * @return a stream of problems with the specified severity + */ + @Nonnull + Stream

problems(BuilderProblem.Severity severity); + + /** + * Creates an "empty" problem collector that doesn't store any problems. + * + * @param

the type of problem + * @return an empty problem collector + */ + @Nonnull + static

ProblemCollector

empty() { + return new ProblemCollector<>() { + @Override + public boolean problemsOverflow() { + return false; + } + + @Override + public int problemsReportedFor(BuilderProblem.Severity... severities) { + return 0; + } + + @Override + public boolean reportProblem(P problem) { + throw new IllegalStateException("empty problem collector"); + } + + @Override + public Stream

problems(BuilderProblem.Severity severity) { + return Stream.empty(); + } + }; + } + + /** + * Creates new instance of problem collector with configuration from the provided session. + * + * @param

the type of problem + * @param protoSession the session containing configuration for the problem collector + * @return a new problem collector instance + */ + @Nonnull + static

ProblemCollector

create(@Nullable ProtoSession protoSession) { + if (protoSession != null + && protoSession.getUserProperties().containsKey(Constants.MAVEN_BUILDER_MAX_PROBLEMS)) { + int limit = Integer.parseInt(protoSession.getUserProperties().get(Constants.MAVEN_BUILDER_MAX_PROBLEMS)); + return create(limit, p -> true); + } else { + return create(100); + } + } + + /** + * Creates new instance of problem collector with the specified maximum problem count limit, + * but only preserves problems that match the given filter. + * + * @param

the type of problem + * @param maxCountLimit the maximum number of problems to preserve + * @param filter predicate to decide which problems to record + * @return a new filtered problem collector instance + */ + @Nonnull + static

ProblemCollector

create(int maxCountLimit, Predicate filter) { + return new Impl<>(maxCountLimit, filter); + } + + /** + * Creates new instance of problem collector with the specified maximum problem count limit. + * Visible for testing only. + * + * @param

the type of problem + * @param maxCountLimit the maximum number of problems to preserve + * @return a new problem collector instance + */ + @Nonnull + static

ProblemCollector

create(int maxCountLimit) { + return create(maxCountLimit, p -> true); + } + + /** + * Default implementation of the ProblemCollector interface. + * + * @param

the type of problem + */ + class Impl

implements ProblemCollector

{ + + private final int maxCountLimit; + private final AtomicInteger totalCount; + private final ConcurrentMap counters; + private final ConcurrentMap> problems; + private final Predicate filter; + + private static final List REVERSED_ORDER = Arrays.stream( + BuilderProblem.Severity.values()) + .sorted(Comparator.reverseOrder()) + .toList(); + + private Impl(int maxCountLimit, Predicate filter) { + if (maxCountLimit < 0) { + throw new IllegalArgumentException("maxCountLimit must be non-negative"); + } + this.maxCountLimit = maxCountLimit; + this.totalCount = new AtomicInteger(); + this.counters = new ConcurrentHashMap<>(); + this.problems = new ConcurrentHashMap<>(); + this.filter = requireNonNull(filter, "filter"); + } + + @Override + public int problemsReportedFor(BuilderProblem.Severity... severity) { + int result = 0; + for (BuilderProblem.Severity s : severity) { + result += getCounter(s).intValue(); + } + return result; + } + + @Override + public boolean problemsOverflow() { + return totalCount.get() > maxCountLimit; + } + + @Override + public boolean reportProblem(P problem) { + requireNonNull(problem, "problem"); + // first apply filter + if (!filter.test(problem)) { + // drop without counting towards preserved problems + return false; + } + int currentCount = totalCount.incrementAndGet(); + getCounter(problem.getSeverity()).increment(); + if (currentCount <= maxCountLimit || dropProblemWithLowerSeverity(problem.getSeverity())) { + getProblems(problem.getSeverity()).add(problem); + return true; + } + return false; + } + + @Override + public Stream

problems(BuilderProblem.Severity severity) { + requireNonNull(severity, "severity"); + return getProblems(severity).stream(); + } + + private LongAdder getCounter(BuilderProblem.Severity severity) { + return counters.computeIfAbsent(severity, k -> new LongAdder()); + } + + private List

getProblems(BuilderProblem.Severity severity) { + return problems.computeIfAbsent(severity, k -> new CopyOnWriteArrayList<>()); + } + + private boolean dropProblemWithLowerSeverity(BuilderProblem.Severity severity) { + for (BuilderProblem.Severity s : REVERSED_ORDER) { + if (s.ordinal() > severity.ordinal()) { + List

problems = getProblems(s); + while (!problems.isEmpty()) { + try { + return problems.remove(0) != null; + } catch (IndexOutOfBoundsException e) { + // empty, continue + } + } + } + } + return false; + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilder.java new file mode 100644 index 000000000000..518033b601af --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilder.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; + +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * @since 4.0.0 + */ +@Experimental +public interface ProjectBuilder extends Service { + + /** + * Creates a {@link org.apache.maven.api.Project} from a POM file. + * + * @param request {@link ProjectBuilderRequest} + * @return the {@link ProjectBuilderResult} containing the built project and possible errors + * @throws ProjectBuilderException if the project cannot be created + * @throws IllegalArgumentException if an argument is {@code null} or invalid + */ + @Nonnull + ProjectBuilderResult build(ProjectBuilderRequest request) throws ProjectBuilderException; + + /** + * Creates a {@link org.apache.maven.api.Project} from a POM file. + * + * @param session the {@link Session}, must not be {@code null} + * @param source The {@link Source}, must not be {@code null} + * @throws ProjectBuilderException if the project cannot be created + * @throws IllegalArgumentException if an argument is {@code null} or invalid + * @see #build(ProjectBuilderRequest) + */ + @Nonnull + default ProjectBuilderResult build(@Nonnull Session session, @Nonnull Source source) + throws ProjectBuilderException { + return build(ProjectBuilderRequest.build(session, source)); + } + + /** + * Creates a {@link org.apache.maven.api.Project} from a POM file. + * + * @param session the {@link Session}, must not be {@code null} + * @param path the {@link Path}, must not be {@code null} + * @throws ProjectBuilderException if the project cannot be created + * @throws IllegalArgumentException if an argument is {@code null} or invalid + * @see #build(ProjectBuilderRequest) + */ + @Nonnull + default ProjectBuilderResult build(@Nonnull Session session, @Nonnull Path path) throws ProjectBuilderException { + return build(ProjectBuilderRequest.build(session, path)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java new file mode 100644 index 000000000000..d6adcaee2a44 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link ProjectBuilder} service. + * + * @since 4.0.0 + */ +@Experimental +public class ProjectBuilderException extends MavenException { + + @Serial + private static final long serialVersionUID = -7629871850875943799L; + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public ProjectBuilderException(String message, Exception e) { + super(message, e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java new file mode 100644 index 000000000000..82129b4f1b69 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java @@ -0,0 +1,331 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * Request used to build a {@link org.apache.maven.api.Project} using + * the {@link ProjectBuilder} service. + * + * TODO: add validationLevel, activeProfileIds, inactiveProfileIds, resolveDependencies + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface ProjectBuilderRequest extends Request { + + /** + * Gets the path to the project to build. + * This is typically the path to a pom.xml file or a directory containing a pom.xml file. + * + * @return an optional containing the path to the project, or empty if not specified + */ + @Nonnull + Optional getPath(); + + /** + * Gets the source of the project to build. + * This is an alternative to specifying a path, allowing the project to be built from + * a model source such as a string or input stream. + * + * @return an optional containing the source of the project, or empty if not specified + */ + @Nonnull + Optional getSource(); + + /** + * Determines whether a stub model should be allowed when the POM is missing or unreadable. + * A stub model contains only minimal information derived from the project's coordinates. + * + * @return true if a stub model should be allowed, false otherwise + */ + boolean isAllowStubModel(); + + /** + * Determines whether the project builder should recursively build parent/child projects. + * When true, the builder will process parent POMs and child modules as needed. + * + * @return true if the build should be recursive, false otherwise + */ + boolean isRecursive(); + + /** + * Determines whether plugins should be processed during project building. + * When true, the builder will process plugin information which may include + * resolving plugin dependencies and executing plugin goals that participate in project building. + * + * @return true if plugins should be processed, false otherwise + */ + boolean isProcessPlugins(); + + /** + * Gets the list of remote repositories to use for resolving dependencies during project building. + * These repositories will be used in addition to any repositories defined in the project itself. + * + * @return the list of remote repositories, or null if not specified + */ + @Nullable + List getRepositories(); + + /** + * Creates a new ProjectBuilderRequest with the specified session and source. + * + * @param session the Maven session + * @param source the source of the project to build + * @return a new ProjectBuilderRequest + * @throws NullPointerException if session or source is null + */ + @Nonnull + static ProjectBuilderRequest build(@Nonnull Session session, @Nonnull Source source) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .source(requireNonNull(source, "source cannot be null")) + .build(); + } + + /** + * Creates a new ProjectBuilderRequest with the specified session and path. + * + * @param session the Maven session + * @param path the path to the project to build + * @return a new ProjectBuilderRequest + * @throws NullPointerException if session or path is null + */ + @Nonnull + static ProjectBuilderRequest build(@Nonnull Session session, @Nonnull Path path) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .path(requireNonNull(path, "path cannot be null")) + .build(); + } + + /** + * Creates a new builder for constructing a ProjectBuilderRequest. + * + * @return a new ProjectBuilderRequestBuilder + */ + @Nonnull + static ProjectBuilderRequestBuilder builder() { + return new ProjectBuilderRequestBuilder(); + } + + /** + * Builder for creating ProjectBuilderRequest instances. + * This builder provides a fluent API for setting the various properties of a request. + */ + @NotThreadSafe + class ProjectBuilderRequestBuilder { + Session session; + RequestTrace trace; + Path path; + Source source; + boolean allowStubModel; + boolean recursive; + boolean processPlugins = true; + List repositories; + + ProjectBuilderRequestBuilder() {} + + /** + * Sets the Maven session for this request. + * + * @param session the Maven session + * @return this builder instance + */ + public ProjectBuilderRequestBuilder session(Session session) { + this.session = session; + return this; + } + + /** + * Sets the request trace for this request. + * The trace is used for debugging and monitoring purposes. + * + * @param trace the request trace + * @return this builder instance + */ + public ProjectBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + /** + * Sets the path to the project to build. + * This is typically the path to a pom.xml file or a directory containing a pom.xml file. + * + * @param path the path to the project + * @return this builder instance + */ + public ProjectBuilderRequestBuilder path(Path path) { + this.path = path; + return this; + } + + /** + * Sets the source of the project to build. + * This is an alternative to specifying a path, allowing the project to be built from + * a model source such as a string or input stream. + * + * @param source the source of the project + * @return this builder instance + */ + public ProjectBuilderRequestBuilder source(Source source) { + this.source = source; + return this; + } + + /** + * Sets whether plugins should be processed during project building. + * When true, the builder will process plugin information which may include + * resolving plugin dependencies and executing plugin goals that participate in project building. + * + * @param processPlugins true if plugins should be processed, false otherwise + * @return this builder instance + */ + public ProjectBuilderRequestBuilder processPlugins(boolean processPlugins) { + this.processPlugins = processPlugins; + return this; + } + + /** + * Sets the list of remote repositories to use for resolving dependencies during project building. + * These repositories will be used in addition to any repositories defined in the project itself. + * + * @param repositories the list of remote repositories + * @return this builder instance + */ + public ProjectBuilderRequestBuilder repositories(List repositories) { + this.repositories = repositories; + return this; + } + + /** + * Builds a new ProjectBuilderRequest with the current builder settings. + * + * @return a new ProjectBuilderRequest instance + */ + public ProjectBuilderRequest build() { + return new DefaultProjectBuilderRequest( + session, trace, path, source, allowStubModel, recursive, processPlugins, repositories); + } + + private static class DefaultProjectBuilderRequest extends BaseRequest + implements ProjectBuilderRequest { + private final Path path; + private final Source source; + private final boolean allowStubModel; + private final boolean recursive; + private final boolean processPlugins; + private final List repositories; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultProjectBuilderRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nullable Path path, + @Nullable Source source, + boolean allowStubModel, + boolean recursive, + boolean processPlugins, + @Nullable List repositories) { + super(session, trace); + this.path = path; + this.source = source; + this.allowStubModel = allowStubModel; + this.recursive = recursive; + this.processPlugins = processPlugins; + this.repositories = repositories; + } + + @Nonnull + @Override + public Optional getPath() { + return Optional.ofNullable(path); + } + + @Nonnull + @Override + public Optional getSource() { + return Optional.ofNullable(source); + } + + @Override + public boolean isAllowStubModel() { + return allowStubModel; + } + + @Override + public boolean isRecursive() { + return recursive; + } + + @Override + public boolean isProcessPlugins() { + return processPlugins; + } + + @Override + public List getRepositories() { + return repositories; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultProjectBuilderRequest that + && allowStubModel == that.allowStubModel + && recursive == that.recursive + && processPlugins == that.processPlugins + && Objects.equals(path, that.path) + && Objects.equals(source, that.source) + && Objects.equals(repositories, that.repositories); + } + + @Override + public int hashCode() { + return Objects.hash(path, source, allowStubModel, recursive, processPlugins, repositories); + } + + @Override + public String toString() { + return "ProjectBuilderRequest[" + "path=" + + path + ", source=" + + source + ", allowStubModel=" + + allowStubModel + ", recursive=" + + recursive + ", processPlugins=" + + processPlugins + ", repositories=" + + repositories + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java new file mode 100644 index 000000000000..1cc43440e940 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Optional; + +import org.apache.maven.api.Project; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Result of a project build call. + * + * @since 4.0.0 + */ +@Experimental +public interface ProjectBuilderResult extends Result { + + /** + * Gets the identifier of the project that could not be built. The general format of the identifier is {@code + * ::} but some of these coordinates may still be unknown at the point the exception + * is thrown so this information is merely meant to assist the user. + * + * @return the identifier of the project or an empty string if not known, never {@code null} + */ + @Nonnull + String getProjectId(); + + /** + * Gets the POM file from which the project was built. + * + * @return the optional POM file + */ + @Nonnull + Optional getPomFile(); + + /** + * Gets the project that was built. + * + * @return The project that was built or {@code null} if an error occurred and this result accompanies a + * {@link ProjectBuilderException}. + */ + @Nonnull + Optional getProject(); + + /** + * Gets the problems that were encountered during the project building. + * + * @return the problems that were encountered during the project building, can be empty but never {@code null} + */ + @Nonnull + Collection getProblems(); + + /** + * Gets the result of the dependency resolution for the project. + * + * @return the result of the dependency resolution for the project + */ + @Nonnull + Optional getDependencyResolverResult(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectManager.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectManager.java new file mode 100644 index 000000000000..302768585d83 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectManager.java @@ -0,0 +1,274 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.maven.api.Language; +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.Project; +import org.apache.maven.api.ProjectScope; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.SourceRoot; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Interface to manage the project state and artifacts during the Maven build lifecycle. + * This service provides operations to: + *

    + *
  • Manage project artifacts (main and attached)
  • + *
  • Handle source roots and resources
  • + *
  • Access and modify project properties
  • + *
  • Manage repository configurations
  • + *
  • Handle project forking states
  • + *
+ * + * The service maintains the mutable state of projects as they progress through + * their build lifecycle, ensuring thread-safety and proper state management. + * All implementations must be thread-safe as they may be accessed concurrently + * during parallel builds. + * + * @since 4.0.0 + * @see org.apache.maven.api.services.ProjectBuilder + * @see Project + */ +@Experimental +public interface ProjectManager extends Service { + /** + * Returns the path to the built project artifact file, if the project has been built. + * This path is only available after the artifact has been produced during the build lifecycle. + * + * @param project the project to get the artifact path for + * @return an Optional containing the path to the built artifact if available, + * or empty if the artifact hasn't been built yet + */ + @Nonnull + Optional getPath(@Nonnull Project project); + + /** + * Returns an immutable collection of attached artifacts for the given project. + * Attached artifacts are secondary artifacts produced during the build (e.g., sources jar, + * javadoc jar, test jars). These artifacts are created and attached during specific + * lifecycle phases, so the collection contents depend on the build phase when this method + * is called. + * + * @param project the project to get attached artifacts for + * @return an immutable collection of attached artifacts, may be empty if no artifacts + * have been attached yet + * @throws IllegalArgumentException if the project is null + * @see #getAllArtifacts(Project) + */ + @Nonnull + Collection getAttachedArtifacts(@Nonnull Project project); + + /** + * Returns project's all artifacts as an immutable ordered collection. The collection contains: + *
    + *
  • The project's artifacts ({@link Project#getArtifacts()}): + *
      + *
    • The POM artifact (always present)
    • + *
    • The main project artifact (if applicable based on packaging)
    • + *
    + *
  • + *
  • All attached artifacts in the order they were attached
  • + *
+ * The contents depend on the current lifecycle phase when this method is called, as artifacts + * are typically attached during specific phases (e.g., sources jar during package phase). + * + * @param project the project to get artifacts for + * @return an immutable ordered collection of all project artifacts + * @see #getAttachedArtifacts(Project) + */ + @Nonnull + Collection getAllArtifacts(@Nonnull Project project); + + /** + * Attaches an artifact to the project using the given file path. The artifact type will be + * determined from the file extension. This method is thread-safe and ensures proper + * synchronization of the project's artifact state. + * + * @param session the current build session + * @param project the project to attach the artifact to + * @param path the path to the artifact file + */ + default void attachArtifact(@Nonnull Session session, @Nonnull Project project, @Nonnull Path path) { + String name = path.getFileName().toString(); + int dot = name.lastIndexOf('.'); + String ext = dot >= 1 ? name.substring(dot + 1) : ""; + ProducedArtifact artifact = session.createProducedArtifact( + project.getGroupId(), project.getArtifactId(), project.getVersion(), ext); + attachArtifact(project, artifact, path); + } + + /** + * Attaches an artifact to the project with an explicitly specified type. + * + * @param session the current build session + * @param project the project to attach the artifact to + * @param type the type of the artifact (e.g., "jar", "war", "sources") + * @param path the path to the artifact file + * @see org.apache.maven.api.Type + */ + default void attachArtifact( + @Nonnull Session session, @Nonnull Project project, @Nonnull String type, @Nonnull Path path) { + ProducedArtifact artifact = session.createProducedArtifact( + project.getGroupId(), project.getArtifactId(), project.getVersion(), null, null, type); + attachArtifact(project, artifact, path); + } + + /** + * Attaches a produced artifact to the project at the specified path. This is the base method + * that the other attachArtifact methods delegate to. + * + * @param project the project to attach the artifact to + * @param artifact the produced artifact to attach + * @param path the path to the artifact file + */ + void attachArtifact(@Nonnull Project project, @Nonnull ProducedArtifact artifact, @Nonnull Path path); + + /** + * {@return all source root directories}, including the disabled ones, for all languages and scopes. + * For listing only the {@linkplain SourceRoot#enabled() enabled} source roots, + * the following code can be used: + * + *
{@literal
+     * List enabledRoots = project.getSourceRoots()
+     *         .stream().filter(SourceRoot::enabled).toList();
+     * }
+ * + * The iteration order is the order in which the sources are declared in the POM file. + * + * @param project the project for which to get the source roots + */ + @Nonnull + Collection getSourceRoots(@Nonnull Project project); + + /** + * {@return all enabled sources that provide files in the given language for the given scope}. + * If the given scope is {@code null}, then this method returns the enabled sources for all scopes. + * If the given language is {@code null}, then this method returns the enabled sources for all languages. + * An arbitrary number of source roots may exist for the same scope and language. + * It may be, for example, the case of a multi-versions project. + * The iteration order is the order in which the sources are declared in the POM file. + * + * @param project the project for which to get the enabled source roots + * @param scope the scope of the sources to return, or {@code null} for all scopes + * @param language the language of the sources to return, or {@code null} for all languages + */ + @Nonnull + Stream getEnabledSourceRoots( + @Nonnull Project project, @Nullable ProjectScope scope, @Nullable Language language); + + /** + * Adds the given source to the given project. + * If a source already exists for the given scope, language and directory, + * then the behavior depends on the {@code ProjectManager} implementation. + * It may do nothing or thrown {@linkplain IllegalArgumentException}. + * + * @param project the project to update + * @param source the source to add + * @throws IllegalArgumentException if this project manager rejects the given source because of conflict + * + * @see #getSourceRoots(Project) + */ + void addSourceRoot(@Nonnull Project project, @Nonnull SourceRoot source); + + /** + * Resolves and adds the given directory as a source with the given scope and language. + * First, this method resolves the given root against the project base directory, then normalizes the path. + * If no source already exists for the same scope, language and normalized directory, + * these arguments are added as a new {@link SourceRoot} element. + * Otherwise (i.e., in case of potential conflict), the behavior depends on the {@code ProjectManager}. + * The default implementation does nothing in the latter case. + * + * @param project the project to update + * @param scope scope (main or test) of the directory to add + * @param language language of the files contained in the directory to add + * @param directory the directory to add if not already present in the source + * + * @see #getEnabledSourceRoots(Project, ProjectScope, Language) + */ + void addSourceRoot( + @Nonnull Project project, @Nonnull ProjectScope scope, @Nonnull Language language, @Nonnull Path directory); + + /** + * Returns an immutable list of project remote repositories (directly specified or inherited). + * The repositories are ordered by declaration order, with inherited repositories appearing + * after directly specified ones. + * + * @param project the project + * @return ordered list of remote repositories + */ + @Nonnull + List getRemoteProjectRepositories(@Nonnull Project project); + + /** + * Returns an immutable list of project plugin remote repositories (directly specified or inherited). + * The repositories are ordered by declaration order, with inherited repositories appearing + * after directly specified ones. + * + * @param project the project + * @return ordered list of remote repositories + */ + @Nonnull + List getRemotePluginRepositories(@Nonnull Project project); + + /** + * {@return an immutable map of the project properties}. + * + * @param project the project for which to get the properties + * + * @see #setProperty(Project, String, String) + */ + @Nonnull + Map getProperties(@Nonnull Project project); + + /** + * Set a given project property. Properties set through this method are only valid + * for the current build session and do not modify the underlying project model. + * + * @param project the project to modify + * @param key they property's key + * @param value the value or {@code null} to unset the property + */ + void setProperty(@Nonnull Project project, @Nonnull String key, @Nullable String value); + + /** + * Returns the original project being built when the input project is a forked project. + * During certain lifecycle phases, particularly for aggregator mojos, Maven may create + * a forked project (a copy of the original project) to execute a subset of the lifecycle. + * This method allows retrieving the original project that initiated the build. + * + * @param project the potentially forked project + * @return an Optional containing the original project if the input is a forked project, + * or an empty Optional if the input is already the original project + * @throws IllegalArgumentException if the project is null + */ + @Nonnull + Optional getExecutionProject(@Nonnull Project project); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectScopeRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectScopeRegistry.java new file mode 100644 index 000000000000..edeae15488f4 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectScopeRegistry.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.ProjectScope; +import org.apache.maven.api.annotations.Experimental; + +/** + * Manager for {@link ProjectScope}. + * + * @since 4.0.0 + */ +@Experimental +public interface ProjectScopeRegistry extends ExtensibleEnumRegistry {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Prompter.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Prompter.java new file mode 100644 index 000000000000..29f2a75fc760 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Prompter.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Service used to interact with the end user. + * + * @since 4.0.0 + */ +@Experimental +public interface Prompter extends Service { + /** + * Prompts the user for a string. + * + * @param message the message to display to the user + * @return the string entered by the user + * @throws PrompterException if an exception occurs + */ + @Nonnull + default String prompt(@Nullable String message) throws PrompterException { + return prompt(message, null, null); + } + + /** + * Prompts the user for a string using a default value. + * + * @param message the message to display + * @param defaultReply the default reply value + * @return the string entered by the user + * @throws PrompterException if an exception occurs + */ + @Nonnull + default String prompt(@Nullable String message, @Nullable String defaultReply) throws PrompterException { + return prompt(message, null, defaultReply); + } + + /** + * Prompts the user for a string using a list of possible values. + * + * @param message the message to display + * @param possibleValues the list of possible values + * @return the string entered by the user + * @throws PrompterException if an exception occurs + */ + @Nonnull + default String prompt(@Nullable String message, @Nullable List possibleValues) throws PrompterException { + return prompt(message, possibleValues, null); + } + + /** + * Prompts the user for a string using a list of possible values and a default reply. + * + * @param message the message to display + * @param possibleValues the list of possible values + * @param defaultReply the default reply value + * @return the string entered by the user + * @throws PrompterException if an exception occurs + */ + @Nonnull + String prompt(@Nullable String message, @Nullable List possibleValues, @Nullable String defaultReply) + throws PrompterException; + + /** + * Prompts the user for a password. + * + * @param message the message to display + * @return the password entered by the user + * @throws PrompterException if an exception occurs + */ + @Nonnull + String promptForPassword(@Nullable String message) throws PrompterException; + + /** + * Displays a message to the user. + * + * @param message the message to display + * @throws PrompterException if an exception occurs + */ + void showMessage(@Nullable String message) throws PrompterException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java new file mode 100644 index 000000000000..ca17937dfcc1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link Prompter} service. + * + * @since 4.0.0 + */ +@Experimental +public class PrompterException extends MavenException { + + @Serial + private static final long serialVersionUID = -3505070928479515081L; + + public PrompterException(String message) { + super(message); + } + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public PrompterException(String message, Exception e) { + super(message, e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/RepositoryFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RepositoryFactory.java new file mode 100644 index 000000000000..abd7ff9670ac --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RepositoryFactory.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; +import java.util.List; + +import org.apache.maven.api.LocalRepository; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Repository; + +/** + * Factory service to create {@link LocalRepository} or {@link RemoteRepository} objects. + * + * @since 4.0.0 + */ +@Experimental +public interface RepositoryFactory extends Service { + + @Nonnull + LocalRepository createLocal(@Nonnull Path path); + + @Nonnull + RemoteRepository createRemote(@Nonnull String id, @Nonnull String url); + + @Nonnull + RemoteRepository createRemote(@Nonnull Repository repository); + + @Nonnull + List aggregate( + @Nonnull Session session, + @Nonnull List dominant, + @Nonnull List recessive, + boolean processRecessive); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Request.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Request.java new file mode 100644 index 000000000000..b65d4a2d3477 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Request.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Base interface for service requests in Maven. This interface defines the common contract + * for all request types within the Maven service layer, providing access to session context + * and request tracing capabilities. + * + *

Each request is associated with a {@link ProtoSession} that contains the configuration + * and context necessary for request processing, including: + *

    + *
  • User and system properties for interpolation
  • + *
  • Session start time information
  • + *
  • Project directory structures
  • + *
+ * + *

Requests can optionally carry trace information through {@link RequestTrace} to support: + *

    + *
  • Debugging and troubleshooting of request flows
  • + *
  • Audit logging of operations
  • + *
  • Performance monitoring of nested operations
  • + *
+ * + *

This interface is designed to be extended by specific request types that handle + * different Maven operations. All implementations must be immutable to ensure thread safety + * and predictable behavior in concurrent environments. + * + * @param the type of ProtoSession associated with this request, allowing for + * type-safe session handling in specific request implementations + * + * @see ProtoSession + * @see RequestTrace + * @see Result + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Request { + + /** + * Returns the session associated with this request. + * + * @return the session instance, never {@code null} + */ + @Nonnull + S getSession(); + + /** + * Returns the trace information associated with this request, if any. + * The trace provides context about the request's position in the operation + * hierarchy and can carry additional diagnostic information. + * + * @return the request trace, or {@code null} if no trace information is available + */ + @Nullable + RequestTrace getTrace(); + + /** + * Returns a hashcode value for this request, based on all significant fields. + * Implementations must ensure that if two requests are equal according to + * {@link #equals(Object)}, they have the same hashcode. + * + * @return a hash code value for this request + */ + @Override + int hashCode(); + + /** + * Returns {@code true} if the specified object is equal to this request. + * Two requests are considered equal if they have the same type and all + * significant fields are equal. + * + * @param obj the object to compare with this request + * @return {@code true} if the objects are equal, {@code false} otherwise + */ + @Override + boolean equals(Object obj); + + /** + * Returns a string representation of this request, used for debugging and logging purposes. + * The format should include the request type and any significant attributes that define the + * request's state. + * + * @return a string representation of this request, never {@code null} + */ + @Override + @Nonnull + String toString(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java new file mode 100644 index 000000000000..6dafc3aeaf57 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Nullable; + +/** + * Represents a hierarchical trace of nested requests within a session, enabling correlation between + * session events and their originating operations in the application code. The trace structure + * supports the following key features: + * + *

    + *
  • Maintains parent-child relationships between requests to track operation nesting
  • + *
  • Carries contextual data describing the current request or operation
  • + *
  • Supports both internal session operations and client-provided trace information
  • + *
+ * + *

For internal session operations, the trace typically contains {@code *Request} objects + * that represent the current processing state. Client code can also create traces with + * application-specific data to provide context when invoking session methods.

+ * + *

This trace information is particularly useful for:

+ *
    + *
  • Debugging and troubleshooting request flows
  • + *
  • Audit logging of session operations
  • + *
  • Performance monitoring of nested operations
  • + *
+ * + * @param context The context identifier for this request trace, helping to identify the scope or purpose + * of the request. May be null if no specific context is needed. + * @param parent The parent request trace that led to this request, establishing the chain of nested + * operations. May be null for top-level requests. + * @param data Additional data associated with this request trace, typically containing the actual request + * object being processed or any application-specific state information. May be null if no + * additional data is needed. + */ +public record RequestTrace(@Nullable String context, @Nullable RequestTrace parent, @Nullable Object data) { + + public static final String CONTEXT_PLUGIN = "plugin"; + public static final String CONTEXT_PROJECT = "project"; + public static final String CONTEXT_BOOTSTRAP = "bootstrap"; + + public RequestTrace(RequestTrace parent, Object data) { + this(parent != null ? parent.context() : null, parent, data); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Result.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Result.java new file mode 100644 index 000000000000..65bdf97a91ab --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Result.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Base interface for service operation results in Maven. This interface defines the common contract + * for operation results, providing access to the original request that generated this result. + * + *

Each result is linked to its originating {@link Request}, allowing for: + *

    + *
  • Traceability between requests and their outcomes
  • + *
  • Access to the session context used during processing
  • + *
  • Correlation of results with their initiating parameters
  • + *
+ * + * @param the type of Request that produced this result, ensuring type-safe + * access to the original request parameters + * + * @see Request + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Result> { + + /** + * Returns the request that produced this result. + * + * @return the originating request instance, never {@code null} + */ + @Nonnull + REQ getRequest(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java new file mode 100644 index 000000000000..000ebb883780 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Path; + +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.settings.Settings; + +/** + * Builds the effective settings from a user settings file and/or an installation settings file. + * + * @since 4.0.0 + */ +@Experimental +public interface SettingsBuilder extends Service { + + /** + * Builds the effective settings of the specified settings files. + * + * @param request the settings building request that holds the parameters, must not be {@code null} + * @return the result of the settings building, never {@code null} + * @throws SettingsBuilderException if the effective settings could not be built + */ + @Nonnull + SettingsBuilderResult build(@Nonnull SettingsBuilderRequest request); + + /** + * Builds the effective settings of the specified settings sources. + * + * @return the result of the settings building, never {@code null} + * @throws SettingsBuilderException if the effective settings could not be built + */ + @Nonnull + default SettingsBuilderResult build( + @Nonnull Session session, @Nonnull Source installationSettingsSource, @Nonnull Source userSettingsSource) { + return build(session, installationSettingsSource, null, userSettingsSource); + } + + /** + * Builds the effective settings of the specified settings paths. + * + * @return the result of the settings building, never {@code null} + * @throws SettingsBuilderException if the effective settings could not be built + */ + @Nonnull + default SettingsBuilderResult build( + @Nonnull Session session, @Nonnull Path installationSettingsPath, @Nonnull Path userSettingsPath) { + return build(session, installationSettingsPath, null, userSettingsPath); + } + + /** + * Builds the effective settings of the specified settings sources. + * + * @return the result of the settings building, never {@code null} + * @throws SettingsBuilderException if the effective settings could not be built + */ + @Nonnull + default SettingsBuilderResult build( + @Nonnull Session session, + @Nonnull Source installationSettingsSource, + @Nonnull Source projectSettingsSource, + @Nonnull Source userSettingsSource) { + return build(SettingsBuilderRequest.build( + session, installationSettingsSource, projectSettingsSource, userSettingsSource)); + } + + /** + * Builds the effective settings of the specified settings paths. + * + * @return the result of the settings building, never {@code null} + * @throws SettingsBuilderException if the effective settings could not be built + */ + @Nonnull + default SettingsBuilderResult build( + @Nonnull Session session, + @Nonnull Path installationSettingsPath, + @Nonnull Path projectSettingsPath, + @Nonnull Path userSettingsPath) { + return build( + SettingsBuilderRequest.build(session, installationSettingsPath, projectSettingsPath, userSettingsPath)); + } + + /** + * Validate the specified settings. + * + * @param settings The settings to validate, must not be {@code null}. + * @return The list of problems that were encountered, must not be {@code null}. + */ + @Nonnull + default ProblemCollector validate(@Nonnull Settings settings) { + return validate(settings, false); + } + + /** + * Validate the specified settings. + * + * @param settings The settings to validate, must not be {@code null}. + * @param isProjectSettings Boolean indicating if the validation is for project settings or user / installation settings. + * @return The list of problems that were encountered, must not be {@code null}. + */ + @Nonnull + ProblemCollector validate(@Nonnull Settings settings, boolean isProjectSettings); + + /** + * Convert a model profile to a settings profile. + */ + @Nonnull + org.apache.maven.api.settings.Profile convert(@Nonnull org.apache.maven.api.model.Profile profile); + + /** + * Convert a settings profile to a model profile. + */ + @Nonnull + org.apache.maven.api.model.Profile convert(@Nonnull org.apache.maven.api.settings.Profile profile); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java new file mode 100644 index 000000000000..a8ccd0f47f53 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link SettingsBuilder}. + * + * @since 4.0.0 + */ +@Experimental +public class SettingsBuilderException extends MavenBuilderException { + + @Serial + private static final long serialVersionUID = 4714858598345418083L; + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public SettingsBuilderException(String message, Exception e) { + super(message, e); + } + + public SettingsBuilderException(String message, ProblemCollector problems) { + super(message, problems); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java new file mode 100644 index 000000000000..1ab8a9a15b2a --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; +import java.util.function.UnaryOperator; + +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * Collects settings that control the building of effective settings. + */ +@Experimental +@Immutable +public interface SettingsBuilderRequest extends Request { + + /** + * Gets the installation settings source. + * + * @return the installation settings source or {@code null} if none + */ + @Nonnull + Optional getInstallationSettingsSource(); + + /** + * Gets the project settings source. + * + * @return the project settings source or {@code null} if none + */ + @Nonnull + Optional getProjectSettingsSource(); + + /** + * Gets the user settings source. + * + * @return the user settings source or {@code null} if none + */ + @Nonnull + Optional getUserSettingsSource(); + + /** + * The optional interpolation source used for interpolation. + * + * @return the interpolation source for interpolation + */ + @Nonnull + Optional> getInterpolationSource(); + + @Nonnull + static SettingsBuilderRequest build( + @Nonnull ProtoSession session, + @Nonnull Source installationSettingsSource, + @Nonnull Source userSettingsSource) { + return build(session, installationSettingsSource, null, userSettingsSource); + } + + @Nonnull + static SettingsBuilderRequest build( + @Nonnull ProtoSession session, @Nonnull Path installationSettingsPath, @Nonnull Path userSettingsPath) { + return build(session, Sources.fromPath(installationSettingsPath), null, Sources.fromPath(userSettingsPath)); + } + + @Nonnull + static SettingsBuilderRequest build( + @Nonnull ProtoSession session, + @Nullable Source installationSettingsSource, + @Nullable Source projectSettingsSource, + @Nullable Source userSettingsSource) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .installationSettingsSource(installationSettingsSource) + .projectSettingsSource(projectSettingsSource) + .userSettingsSource(userSettingsSource) + .build(); + } + + @Nonnull + static SettingsBuilderRequest build( + @Nonnull ProtoSession session, + @Nullable Path installationSettingsPath, + @Nullable Path projectSettingsPath, + @Nullable Path userSettingsPath) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .installationSettingsSource( + installationSettingsPath != null && Files.exists(installationSettingsPath) + ? Sources.fromPath(installationSettingsPath) + : null) + .projectSettingsSource( + projectSettingsPath != null && Files.exists(projectSettingsPath) + ? Sources.fromPath(projectSettingsPath) + : null) + .userSettingsSource( + userSettingsPath != null && Files.exists(userSettingsPath) + ? Sources.fromPath(userSettingsPath) + : null) + .build(); + } + + @Nonnull + static SettingsBuilderRequestBuilder builder() { + return new SettingsBuilderRequestBuilder(); + } + + @NotThreadSafe + class SettingsBuilderRequestBuilder { + ProtoSession session; + RequestTrace trace; + Source installationSettingsSource; + Source projectSettingsSource; + Source userSettingsSource; + UnaryOperator interpolationSource; + + public SettingsBuilderRequestBuilder session(ProtoSession session) { + this.session = session; + return this; + } + + public SettingsBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public SettingsBuilderRequestBuilder installationSettingsSource(Source installationSettingsSource) { + this.installationSettingsSource = installationSettingsSource; + return this; + } + + public SettingsBuilderRequestBuilder projectSettingsSource(Source projectSettingsSource) { + this.projectSettingsSource = projectSettingsSource; + return this; + } + + public SettingsBuilderRequestBuilder userSettingsSource(Source userSettingsSource) { + this.userSettingsSource = userSettingsSource; + return this; + } + + public SettingsBuilderRequestBuilder interpolationSource(UnaryOperator interpolationSource) { + this.interpolationSource = interpolationSource; + return this; + } + + public SettingsBuilderRequest build() { + return new DefaultSettingsBuilderRequest( + session, + trace, + installationSettingsSource, + projectSettingsSource, + userSettingsSource, + interpolationSource); + } + + private static class DefaultSettingsBuilderRequest extends BaseRequest + implements SettingsBuilderRequest { + private final Source installationSettingsSource; + private final Source projectSettingsSource; + private final Source userSettingsSource; + private final UnaryOperator interpolationSource; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultSettingsBuilderRequest( + @Nonnull ProtoSession session, + @Nullable RequestTrace trace, + @Nullable Source installationSettingsSource, + @Nullable Source projectSettingsSource, + @Nullable Source userSettingsSource, + @Nullable UnaryOperator interpolationSource) { + super(session, trace); + this.installationSettingsSource = installationSettingsSource; + this.projectSettingsSource = projectSettingsSource; + this.userSettingsSource = userSettingsSource; + this.interpolationSource = interpolationSource; + } + + @Nonnull + @Override + public Optional getInstallationSettingsSource() { + return Optional.ofNullable(installationSettingsSource); + } + + @Nonnull + @Override + public Optional getProjectSettingsSource() { + return Optional.ofNullable(projectSettingsSource); + } + + @Nonnull + @Override + public Optional getUserSettingsSource() { + return Optional.ofNullable(userSettingsSource); + } + + @Nonnull + @Override + public Optional> getInterpolationSource() { + return Optional.ofNullable(interpolationSource); + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultSettingsBuilderRequest that + && Objects.equals(installationSettingsSource, that.installationSettingsSource) + && Objects.equals(projectSettingsSource, that.projectSettingsSource) + && Objects.equals(userSettingsSource, that.userSettingsSource) + && Objects.equals(interpolationSource, that.interpolationSource); + } + + @Override + public int hashCode() { + return Objects.hash( + installationSettingsSource, projectSettingsSource, userSettingsSource, interpolationSource); + } + + @Override + public String toString() { + return "SettingsBuilderRequest[" + "installationSettingsSource=" + + installationSettingsSource + ", projectSettingsSource=" + + projectSettingsSource + ", userSettingsSource=" + + userSettingsSource + ", interpolationSource=" + + interpolationSource + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java new file mode 100644 index 000000000000..37b6213da287 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.settings.Settings; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface SettingsBuilderResult extends Result { + + /** + * Gets the assembled settings. + * + * @return the assembled settings, never {@code null} + */ + @Nonnull + Settings getEffectiveSettings(); + + /** + * Gets the problems that were encountered during the settings building. Note that only problems of severity + * {@link BuilderProblem.Severity#WARNING} and below are reported here. Problems with a higher severity level cause + * the settings builder to fail with a {@link SettingsBuilderException}. + * + * @return the problems that were encountered during the settings building, can be empty but never {@code null} + */ + @Nonnull + ProblemCollector getProblems(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Source.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Source.java new file mode 100644 index 000000000000..2893faa4e248 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Source.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Provides access to the contents of a source independently of the + * backing store (e.g. file system, database, memory). + *

+ * This is mainly used to parse files into objects such as Maven projects, + * models, settings, or toolchains. The source implementation handles + * all the details of accessing the underlying content while providing + * a uniform API to consumers. + *

+ * Sources can represent: + *

    + *
  • Local filesystem files
  • + *
  • In-memory content
  • + *
  • Database entries
  • + *
  • Network resources
  • + *
+ * + * @since 4.0.0 + * @see org.apache.maven.api.services.ProjectBuilder#build(Session, Source) + */ +@Experimental +public interface Source { + /** + * Provides access to the file backing this source, if available. + * Not all sources are backed by files - for example, in-memory sources + * or database-backed sources will return null. + * + * @return the underlying {@code Path} if this source is file-backed, + * or {@code null} if this source has no associated file + */ + @Nullable + Path getPath(); + + /** + * Creates a new input stream to read the source contents. + * Each call creates a fresh stream starting from the beginning. + * The caller is responsible for closing the returned stream. + * + * @return a new input stream positioned at the start of the content + * @throws IOException if the stream cannot be created or opened + */ + @Nonnull + InputStream openStream() throws IOException; + + /** + * Returns a human-readable description of where this source came from, + * used primarily for error messages and debugging. + *

+ * Examples of locations: + *

    + *
  • Absolute file path: {@code /path/to/pom.xml}
  • + *
  • Relative file path: {@code ../parent/pom.xml}
  • + *
  • URL: {@code https://repo.maven.org/.../pom.xml}
  • + *
  • Description: {@code } or {@code }
  • + *
+ * + * @return a non-null string describing the source location + */ + @Nonnull + String getLocation(); + + /** + * Resolves a new source relative to this one. + *

+ * The resolution strategy depends on the source type: + *

    + *
  • File sources resolve against their parent directory
  • + *
  • URL sources resolve against their base URL
  • + *
  • Other sources may not support resolution and return null
  • + *
+ *

+ * The implementation must handle: + *

    + *
  • Both forward and back slashes as path separators
  • + *
  • Parent directory references (..)
  • + *
  • Both file and directory targets
  • + *
+ * + * @param relative path to resolve relative to this source + * @return the resolved source, or null if resolution not possible + * @throws NullPointerException if relative is null + */ + @Nullable + Source resolve(@Nonnull String relative); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java new file mode 100644 index 000000000000..4acab6006a34 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.cache.CacheMetadata; +import org.apache.maven.api.cache.CacheRetention; + +import static java.util.Objects.requireNonNull; + +/** + * Factory methods for creating different types of sources. + *

+ * This class provides specialized source implementations for different use cases: + *

    + *
  • Path sources - simple access to file content
  • + *
  • Build sources - POM files being actively built by Maven
  • + *
  • Resolved sources - POMs resolved from repositories
  • + *
+ * + * @since 4.0.0 + */ +@Experimental +public final class Sources { + + private Sources() {} + + /** + * Creates a new source for the specified path. + * + * @param path the path to the file + * @return a new Source instance + * @throws NullPointerException if path is null + */ + @Nonnull + public static Source fromPath(@Nonnull Path path) { + return new PathSource(requireNonNull(path, "path")); + } + + /** + * Creates a new build source for the specified path. + * Build sources are used for POM files of projects being built by Maven + * in the filesystem and support resolving related POMs. + * + * @param path the path to the POM file or project directory + * @return a new ModelSource instance configured as a build source + * @throws NullPointerException if path is null + */ + @Nonnull + public static ModelSource buildSource(@Nonnull Path path) { + return new BuildPathSource(requireNonNull(path, "path")); + } + + /** + * Creates a new resolved source for the specified path and location. + * Resolved sources are used for artifacts that have been resolved by Maven + * from repositories (using groupId:artifactId:version coordinates) and + * downloaded to the local repository. These sources do not support resolving + * other sources. + * + * @param path the path to the POM file or project directory + * @param location optional logical location of the source, used for reporting purposes + * @return a new ModelSource instance configured as a resolved source + * @throws NullPointerException if path is null + */ + @Nonnull + public static ModelSource resolvedSource(@Nonnull Path path, @Nullable String location) { + return new ResolvedPathSource(requireNonNull(path, "path"), location); + } + + /** + * Basic implementation of {@link Source} that uses a {@link Path} as the underlying source. + */ + static class PathSource implements Source { + @Nonnull + protected final Path path; + + @Nonnull + protected final String location; + + /** + * Constructs a new PathSource with the specified path. + * + * @param path the filesystem path to the source content + * @throws NullPointerException if path is null + */ + PathSource(Path path) { + this(path, null); + } + + /** + * Constructs a new PathSource with the specified path and location. + * + * @param path the filesystem path to the source content + * @param location the logical location of the source, used for reporting purposes. + * If null, the path string representation is used + */ + protected PathSource(Path path, String location) { + this.path = requireNonNull(path, "path").normalize(); + this.location = location != null ? location : this.path.toString(); + } + + @Override + @Nullable + public Path getPath() { + return path; + } + + @Override + @Nonnull + public InputStream openStream() throws IOException { + return Files.newInputStream(path); + } + + @Override + @Nonnull + public String getLocation() { + return location; + } + + @Override + @Nullable + public Source resolve(@Nonnull String relative) { + return new PathSource(path.resolve(relative)); + } + + @Override + public boolean equals(Object o) { + return o == this || o instanceof PathSource that && Objects.equals(path, that.path); + } + + @Override + public int hashCode() { + return Objects.hash(path); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[location='" + location + "', path=" + path + ']'; + } + } + + /** + * Implementation of {@link ModelSource} for POM files that have been resolved + * from repositories. Does not support resolving related sources. + */ + static class ResolvedPathSource extends PathSource implements ModelSource { + ResolvedPathSource(Path path, String location) { + super(path, location); + } + + @Override + public Path getPath() { + return null; + } + + @Override + public Source resolve(String relative) { + return null; + } + + @Override + @Nullable + public ModelSource resolve(@Nonnull ModelLocator modelLocator, @Nonnull String relative) { + return null; + } + } + + /** + * Implementation of {@link ModelSource} that extends {@link PathSource} with model-specific + * functionality. This implementation uses request-scoped caching ({@link CacheRetention#REQUEST_SCOPED}) + * since it represents a POM file that is actively being built and may change during the build process. + *

+ * The request-scoped retention policy ensures that: + *

    + *
  • Changes to the POM file during the build are detected
  • + *
  • Cache entries don't persist beyond the current build request
  • + *
  • Memory is freed once the build request completes
  • + *
+ */ + static class BuildPathSource extends PathSource implements ModelSource, CacheMetadata { + + /** + * Constructs a new ModelPathSource. + * + * @param path the filesystem path to the source content + */ + BuildPathSource(Path path) { + super(path, null); + } + + @Override + public Path getPath() { + return path; + } + + @Override + public Source resolve(@Nonnull String relative) { + return new BuildPathSource(path.resolve(relative)); + } + + @Override + @Nullable + public ModelSource resolve(@Nonnull ModelLocator locator, @Nonnull String relative) { + String norm = relative.replace('\\', File.separatorChar).replace('/', File.separatorChar); + Path path = getPath().getParent().resolve(norm); + Path relatedPom = locator.locateExistingPom(path); + if (relatedPom != null) { + return new BuildPathSource(relatedPom); + } + return null; + } + + @Override + public CacheRetention getCacheRetention() { + return CacheRetention.REQUEST_SCOPED; + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProvider.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProvider.java new file mode 100644 index 000000000000..ec52981a6a4b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProvider.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.Model; + +/** + * Provides the super POM that all models implicitly inherit from. + * + * @since 4.0.0 + */ +@Experimental +public interface SuperPomProvider extends Service { + + /** + * Gets the super POM for the specified model version. + * + * @param version The model version to retrieve the super POM for (e.g. "4.0.0"), must not be {@code null}. + * @return The super POM, never {@code null}. + * @throws SuperPomProviderException if the super POM could not be retrieved + */ + @Nonnull + Model getSuperPom(@Nonnull String version); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java new file mode 100644 index 000000000000..8014e28b3ab9 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +/** + * Exceptions thrown by the {@link SuperPomProvider} service. + * + * @since 4.0.0 + */ +public class SuperPomProviderException extends MavenException { + + @Serial + private static final long serialVersionUID = -8659892034004509331L; + + public SuperPomProviderException() { + super(); + } + + public SuperPomProviderException(String message) { + super(message); + } + + public SuperPomProviderException(String message, Throwable cause) { + super(message, cause); + } + + public SuperPomProviderException(Throwable cause) { + super(cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainFactory.java new file mode 100644 index 000000000000..d5640265757f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainFactory.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Optional; + +import org.apache.maven.api.Toolchain; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.toolchain.ToolchainModel; + +/** + * Factory interface for creating toolchain instances from configuration models. + * + *

This factory is responsible for instantiating concrete toolchain implementations + * based on toolchain model configurations or default settings.

+ * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface ToolchainFactory { + /** + * Creates a toolchain instance from the provided model configuration. + * + * @param model The toolchain configuration model + * @return A configured toolchain instance + * @throws ToolchainFactoryException if toolchain creation fails + */ + @Nonnull + Toolchain createToolchain(@Nonnull ToolchainModel model) throws ToolchainFactoryException; + + /** + * Creates a default toolchain instance using system defaults. + * + * @return Optional containing the default toolchain if available + * @throws ToolchainFactoryException if default toolchain creation fails + */ + @Nonnull + Optional createDefaultToolchain() throws ToolchainFactoryException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainFactoryException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainFactoryException.java new file mode 100644 index 000000000000..5af3e098594c --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainFactoryException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +/** + * Exception thrown when toolchain factory operations fail. + * + *

This exception wraps errors that occur during toolchain creation or initialization.

+ */ +public class ToolchainFactoryException extends MavenException { + + public ToolchainFactoryException(String message) { + super(message); + } + + public ToolchainFactoryException(String message, Throwable cause) { + super(message, cause); + } + + public ToolchainFactoryException(Throwable cause) { + super(cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManager.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManager.java new file mode 100644 index 000000000000..b867cd4f084b --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManager.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.Toolchain; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Service interface for managing Maven toolchains, which provide abstraction for different + * build tools and environments. + * + *

A toolchain represents a specific build tool configuration (e.g., JDK, compiler) that can be + * used during the Maven build process. This service allows for retrieving, storing, and managing + * these toolchains.

+ * + * @since 4.0.0 + */ +@Experimental +public interface ToolchainManager extends Service { + + /** + * Retrieves toolchains matching the specified type and requirements. + * + * @param session The Maven session context + * @param type The type of toolchain (e.g., "jdk", "compiler") + * @param requirements Key-value pairs specifying toolchain requirements (e.g., "version": "11") + * @return List of matching toolchains, never null + * @throws ToolchainManagerException if toolchain retrieval fails + */ + @Nonnull + List getToolchains(@Nonnull Session session, String type, Map requirements); + + /** + * Retrieves all toolchains of the specified type without additional requirements. + * + * @param session The Maven session context + * @param type The type of toolchain to retrieve + * @return List of matching toolchains, never null + * @throws ToolchainManagerException if toolchain retrieval fails + */ + @Nonnull + default List getToolchains(@Nonnull Session session, @Nonnull String type) + throws ToolchainManagerException { + return getToolchains(session, type, null); + } + + /** + * Retrieves the currently active toolchain from the build context. + * + * @param session The Maven session context + * @param type The type of toolchain to retrieve + * @return Optional containing the toolchain if found + * @throws ToolchainManagerException if toolchain retrieval fails + */ + @Nonnull + Optional getToolchainFromBuildContext(@Nonnull Session session, @Nonnull String type) + throws ToolchainManagerException; + + /** + * Stores a toolchain in the build context for later retrieval. + * + * @param session The Maven session context + * @param toolchain The toolchain to store + * @throws ToolchainManagerException if storing the toolchain fails + */ + void storeToolchainToBuildContext(@Nonnull Session session, @Nonnull Toolchain toolchain) + throws ToolchainManagerException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java new file mode 100644 index 000000000000..38812cee2c3f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link ToolchainManager}. + * + * @since 4.0.0 + */ +@Experimental +public class ToolchainManagerException extends MavenException { + + @Serial + private static final long serialVersionUID = -9465854226608498L; + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public ToolchainManagerException(String message, Exception e) { + super(message, e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilder.java new file mode 100644 index 000000000000..f9bc989ee877 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilder.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Builds the effective toolchains from a user toolchains file and/or an installation toolchains file. + * + * @since 4.0.0 + */ +@Experimental +public interface ToolchainsBuilder extends Service { + + /** + * Builds the effective toolchains for the specified toolchains files. + * + * @param request the toolchains building request that holds the parameters, must not be {@code null} + * @return the result of the toolchains building, never {@code null} + * @throws ToolchainsBuilderException if the effective toolchains could not be built + */ + ToolchainsBuilderResult build(ToolchainsBuilderRequest request); + + /** + * Builds the effective toolchains for the specified toolchains sources. + * + * @param session the {@link Session}, must not be {@code null} + * @param installationToolchainsFile The {@link Source} pointing to the installation toolchains, must not be {@code null} + * @param userToolchainsSource The {@link Source} pointing to the user toolchains, must not be {@code null} + * @throws ToolchainsBuilderException if the project cannot be created + * @throws IllegalArgumentException if an argument is {@code null} or invalid + * @see #build(ToolchainsBuilderRequest) + */ + @Nonnull + default ToolchainsBuilderResult build( + @Nonnull Session session, + @Nonnull Source installationToolchainsFile, + @Nonnull Source userToolchainsSource) { + return build(ToolchainsBuilderRequest.build(session, installationToolchainsFile, userToolchainsSource)); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java new file mode 100644 index 000000000000..10bd3b5dc5bc --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class throw by the {@link ToolchainsBuilder}. + * + * @since 4.0.0 + */ +@Experimental +public class ToolchainsBuilderException extends MavenBuilderException { + + @Serial + private static final long serialVersionUID = 7899871809665729349L; + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public ToolchainsBuilderException(String message, Exception e) { + super(message, e); + } + + public ToolchainsBuilderException(String message, ProblemCollector problems) { + super(message, problems); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java new file mode 100644 index 000000000000..0257ae6760f1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; + +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface ToolchainsBuilderRequest extends Request { + + /** + * Gets the installation Toolchains source. + * + * @return the installation Toolchains source or {@code null} if none + */ + @Nonnull + Optional getInstallationToolchainsSource(); + + /** + * Gets the user Toolchains source. + * + * @return the user Toolchains source or {@code null} if none + */ + @Nonnull + Optional getUserToolchainsSource(); + + @Nonnull + static ToolchainsBuilderRequest build( + @Nonnull ProtoSession session, + @Nullable Source installationToolchainsFile, + @Nullable Source userToolchainsSource) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .installationToolchainsSource(installationToolchainsFile) + .userToolchainsSource(userToolchainsSource) + .build(); + } + + @Nonnull + static ToolchainsBuilderRequest build( + @Nonnull ProtoSession session, + @Nullable Path installationToolchainsFile, + @Nullable Path userToolchainsPath) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .installationToolchainsSource( + installationToolchainsFile != null && Files.exists(installationToolchainsFile) + ? Sources.fromPath(installationToolchainsFile) + : null) + .userToolchainsSource( + userToolchainsPath != null && Files.exists(userToolchainsPath) + ? Sources.fromPath(userToolchainsPath) + : null) + .build(); + } + + @Nonnull + static ToolchainsBuilderRequestBuilder builder() { + return new ToolchainsBuilderRequestBuilder(); + } + + @NotThreadSafe + class ToolchainsBuilderRequestBuilder { + ProtoSession session; + RequestTrace trace; + Source installationToolchainsSource; + Source userToolchainsSource; + + public ToolchainsBuilderRequestBuilder session(ProtoSession session) { + this.session = session; + return this; + } + + public ToolchainsBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public ToolchainsBuilderRequestBuilder installationToolchainsSource(Source installationToolchainsSource) { + this.installationToolchainsSource = installationToolchainsSource; + return this; + } + + public ToolchainsBuilderRequestBuilder userToolchainsSource(Source userToolchainsSource) { + this.userToolchainsSource = userToolchainsSource; + return this; + } + + public ToolchainsBuilderRequest build() { + return new ToolchainsBuilderRequestBuilder.DefaultToolchainsBuilderRequest( + session, trace, installationToolchainsSource, userToolchainsSource); + } + + private static class DefaultToolchainsBuilderRequest extends BaseRequest + implements ToolchainsBuilderRequest { + private final Source installationToolchainsSource; + private final Source userToolchainsSource; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultToolchainsBuilderRequest( + @Nonnull ProtoSession session, + @Nullable RequestTrace trace, + @Nullable Source installationToolchainsSource, + @Nullable Source userToolchainsSource) { + super(session, trace); + this.installationToolchainsSource = installationToolchainsSource; + this.userToolchainsSource = userToolchainsSource; + } + + @Nonnull + @Override + public Optional getInstallationToolchainsSource() { + return Optional.ofNullable(installationToolchainsSource); + } + + @Nonnull + @Override + public Optional getUserToolchainsSource() { + return Optional.ofNullable(userToolchainsSource); + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultToolchainsBuilderRequest that + && Objects.equals(installationToolchainsSource, that.installationToolchainsSource) + && Objects.equals(userToolchainsSource, that.userToolchainsSource); + } + + @Override + public int hashCode() { + return Objects.hash(installationToolchainsSource, userToolchainsSource); + } + + @Override + public String toString() { + return "ToolchainsBuilderRequest[" + "installationToolchainsSource=" + + installationToolchainsSource + ", userToolchainsSource=" + + userToolchainsSource + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java new file mode 100644 index 000000000000..3cf06a7397c5 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.toolchain.PersistedToolchains; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface ToolchainsBuilderResult extends Result { + /** + * Gets the assembled toolchains. + * + * @return the assembled toolchains, never {@code null} + */ + @Nonnull + PersistedToolchains getEffectiveToolchains(); + + /** + * Gets the problems that were encountered during the settings building. Note that only problems of severity + * {@link BuilderProblem.Severity#WARNING} and below are reported here. Problems with a higher severity level cause + * the settings builder to fail with a {@link ToolchainsBuilderException}. + * + * @return the problems that were encountered during the settings building, can be empty but never {@code null} + */ + @Nonnull + ProblemCollector getProblems(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Transport.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Transport.java new file mode 100644 index 000000000000..a74c39905c5a --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Transport.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Closeable; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Optional; + +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Transport for specified remote repository (using provided remote repository base URI as root). Must be treated as a + * resource, best in try-with-resource block. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface Transport extends Closeable { + /** + * GETs the source URI content into target (does not have to exist, or will be overwritten if exist). The + * source MUST BE relative from the {@link RemoteRepository#getUrl()} root. + * + * @return {@code true} if operation succeeded, {@code false} if source does not exist. + * @throws RuntimeException If failed (and not due source not exists). + */ + boolean get(@Nonnull URI relativeSource, @Nonnull Path target); + + /** + * GETs the source URI content as byte array. The source MUST BE relative from the {@link RemoteRepository#getUrl()} + * root. + * + * @return the byte array if operation succeeded, {@code null} if source does not exist. + * @throws RuntimeException If failed (and not due source not exists). + */ + @Nonnull + Optional getBytes(@Nonnull URI relativeSource); + + /** + * GETs the source URI content as string. The source MUST BE relative from the {@link RemoteRepository#getUrl()} + * root. + * + * @return the string if operation succeeded, {@code null} if source does not exist. + * @throws RuntimeException If failed (and not due source not exists). + */ + @Nonnull + Optional getString(@Nonnull URI relativeSource, @Nonnull Charset charset); + + /** + * GETs the source URI content as string using UTF8 charset. The source MUST BE relative from the + * {@link RemoteRepository#getUrl()} root. + * + * @return the string if operation succeeded, {@code null} if source does not exist. + * @throws RuntimeException If failed (and not due source not exists). + */ + @Nonnull + default Optional getString(@Nonnull URI relativeSource) { + return getString(relativeSource, StandardCharsets.UTF_8); + } + + /** + * PUTs the source file (must exist as file) to target URI. The target MUST BE relative from the + * {@link RemoteRepository#getUrl()} root. + * + * @throws RuntimeException If PUT fails for any reason. + */ + void put(@Nonnull Path source, @Nonnull URI relativeTarget); + + /** + * PUTs the source byte array to target URI. The target MUST BE relative from the + * {@link RemoteRepository#getUrl()} root. + * + * @throws RuntimeException If PUT fails for any reason. + */ + void putBytes(@Nonnull byte[] source, @Nonnull URI relativeTarget); + + /** + * PUTs the source string to target URI. The target MUST BE relative from the + * {@link RemoteRepository#getUrl()} root. + * + * @throws RuntimeException If PUT fails for any reason. + */ + void putString(@Nonnull String source, @Nonnull Charset charset, @Nonnull URI relativeTarget); + + /** + * PUTs the source string using UTF8 charset to target URI. The target MUST BE relative from the + * {@link RemoteRepository#getUrl()} root. + * + * @throws RuntimeException If PUT fails for any reason. + */ + default void putString(@Nonnull String source, @Nonnull URI relativeTarget) { + putString(source, StandardCharsets.UTF_8, relativeTarget); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProvider.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProvider.java new file mode 100644 index 000000000000..7bf6bd9aac43 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProvider.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Transporter provider is a service that provides somewhat trivial transport capabilities backed by Maven internals. + * This API does not try to cover all the requirements out there, just the basic ones, and is intentionally simple. + * If plugin or extension needs anything more complex feature wise (i.e. HTTP range support or alike) it should + * probably roll its own. + *

+ * This implementation is backed by Maven Resolver API, supported protocols and transport selection depends on it. If + * resolver preference regarding transport is altered, it will affect this service as well. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface TransportProvider extends Service { + /** + * Provides new {@link Transport} instance for given {@link RemoteRepository}, if possible. + * + * @throws TransportProviderException if passed in remote repository has invalid remote URL or unsupported protocol. + */ + @Nonnull + Transport transport(@Nonnull Session session, @Nonnull RemoteRepository repository); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java new file mode 100644 index 000000000000..3347d7353975 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; + +/** + * @since 4.0.0 + */ +@Experimental +@Consumer +public class TransportProviderException extends MavenException { + + @Serial + private static final long serialVersionUID = -6066070072576465969L; + + public TransportProviderException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java new file mode 100644 index 000000000000..a8820de48570 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Type; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Access to {@link Type} registry. + *

+ * This registry provides access to all registered artifact types, both standard types + * provided by Maven and custom types registered through SPI provider implementations. + * + * @since 4.0.0 + */ +@Experimental +public interface TypeRegistry extends ExtensibleEnumRegistry { + + /** + * Obtain the {@link Type} from the specified {@code id}. + * If no type is known for {@code id}, the registry will + * create a custom {@code Type} for it. + * + * @param id the id of the type to retrieve + * @return the type + */ + @Nonnull + @Override + default Type require(@Nonnull String id) { + return lookup(id).orElseThrow(() -> new IllegalArgumentException("Unknown extensible enum value '" + id + "'")); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java new file mode 100644 index 000000000000..d344122d7f19 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.Service; +import org.apache.maven.api.Version; +import org.apache.maven.api.VersionConstraint; +import org.apache.maven.api.VersionRange; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Service interface to parse {@link Version} and {@link VersionRange}. + * + * @since 4.0.0 + */ +@Experimental +public interface VersionParser extends Service { + + /** + * Parses the specified version string, for example "1.0". + * + * @param version the version string to parse, must not be {@code null} + * @return the parsed version, never {@code null} + * @throws VersionParserException if the string violates the syntax rules of this scheme + * @see org.apache.maven.api.Session#parseVersion(String) + */ + @Nonnull + Version parseVersion(@Nonnull String version); + + /** + * Parses the specified version range specification, for example "[1.0,2.0)". + * + * @param range the range specification to parse, must not be {@code null} + * @return the parsed version range, never {@code null} + * @throws VersionParserException if the range specification violates the syntax rules of this scheme + */ + @Nonnull + VersionRange parseVersionRange(@Nonnull String range); + + /** + * Parses the specified version constraint specification, for example "1.0" or "[1.0,2.0)". + * + * @param constraint the constraint specification to parse, must not be {@code null} + * @return the parsed version constraint, never {@code null} + * @throws VersionParserException if the range specification violates the syntax rules of this scheme + */ + @Nonnull + VersionConstraint parseVersionConstraint(@Nonnull String constraint); + + /** + * Checks whether a given artifact version is considered a {@code SNAPSHOT} or not. + */ + boolean isSnapshot(@Nonnull String version); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java new file mode 100644 index 000000000000..38bedc12752a --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Experimental; + +/** + * The Exception class thrown by {@link VersionParser}. + * + * @since 4.0.0 + */ +@Experimental +public class VersionParserException extends MavenException { + + @Serial + private static final long serialVersionUID = 1504740189114877333L; + + /** + * @param message the message to give + * @param e the {@link Exception} + */ + public VersionParserException(String message, Exception e) { + super(message, e); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolver.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolver.java new file mode 100644 index 000000000000..aec999dd284f --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolver.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Parses and evaluates version ranges encountered in dependency declarations. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface VersionRangeResolver extends Service { + + /** + * Expands a version range to a list of matching versions, in ascending order. + * For example, resolves "[3.8,4.0)" to "3.8", "3.8.1", "3.8.2". + * The returned list of versions is only dependent on the configured repositories and their contents. + * The supplied request may also refer to a single concrete version rather than a version range. + * In this case though, the result contains simply the (parsed) input version, regardless of the + * repositories and their contents. + * + * @param session the session to use + * @param artifactCoordinates t + * @return the version range resolution result + * @throws VersionResolverException if an errors occurs + */ + @Nonnull + default VersionRangeResolverResult resolve( + @Nonnull Session session, @Nonnull ArtifactCoordinates artifactCoordinates) + throws VersionResolverException { + return resolve(VersionRangeResolverRequest.build(session, artifactCoordinates)); + } + + /** + * Expands a version range to a list of matching versions, in ascending order. + * For example, resolves "[3.8,4.0)" to "3.8", "3.8.1", "3.8.2". + * The returned list of versions is only dependent on the configured repositories and their contents. + * The supplied request may also refer to a single concrete version rather than a version range. + * In this case though, the result contains simply the (parsed) input version, regardless of the + * repositories and their contents. + * + * @param session the session to use + * @param artifactCoordinates t + * @param repositories the repositories to use (if {@code null}, the session repositories are used) + * @return the version range resolution result + * @throws VersionResolverException if an errors occurs + */ + @Nonnull + default VersionRangeResolverResult resolve( + @Nonnull Session session, + @Nonnull ArtifactCoordinates artifactCoordinates, + @Nullable List repositories) + throws VersionResolverException { + return resolve(VersionRangeResolverRequest.build(session, artifactCoordinates, repositories)); + } + + @Nonnull + VersionRangeResolverResult resolve(@Nonnull VersionRangeResolverRequest request) + throws VersionRangeResolverException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java new file mode 100644 index 000000000000..d32d0fae8735 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; + +/** + * @since 4.0.0 + */ +@Experimental +@Consumer +public class VersionRangeResolverException extends MavenException { + + @Serial + private static final long serialVersionUID = 4455478418692494141L; + + public VersionRangeResolverException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java new file mode 100644 index 000000000000..52abe9e89a49 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.Objects; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface VersionRangeResolverRequest extends Request { + + @Nonnull + ArtifactCoordinates getArtifactCoordinates(); + + @Nullable + List getRepositories(); + + @Nonnull + static VersionRangeResolverRequest build( + @Nonnull Session session, @Nonnull ArtifactCoordinates artifactCoordinates) { + return build(session, artifactCoordinates, null); + } + + @Nonnull + static VersionRangeResolverRequest build( + @Nonnull Session session, + @Nonnull ArtifactCoordinates artifactCoordinates, + @Nullable List repositories) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .artifactCoordinates(requireNonNull(artifactCoordinates, "artifactCoordinates cannot be null")) + .repositories(repositories) + .build(); + } + + @Nonnull + static VersionResolverRequestBuilder builder() { + return new VersionResolverRequestBuilder(); + } + + @NotThreadSafe + class VersionResolverRequestBuilder { + Session session; + RequestTrace trace; + ArtifactCoordinates artifactCoordinates; + List repositories; + + public VersionResolverRequestBuilder session(Session session) { + this.session = session; + return this; + } + + public VersionResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public VersionResolverRequestBuilder artifactCoordinates(ArtifactCoordinates artifactCoordinates) { + this.artifactCoordinates = artifactCoordinates; + return this; + } + + public VersionResolverRequestBuilder repositories(List repositories) { + this.repositories = repositories; + return this; + } + + public VersionRangeResolverRequest build() { + return new DefaultVersionResolverRequest(session, trace, artifactCoordinates, repositories); + } + + private static class DefaultVersionResolverRequest extends BaseRequest + implements VersionRangeResolverRequest { + private final ArtifactCoordinates artifactCoordinates; + private final List repositories; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultVersionResolverRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull ArtifactCoordinates artifactCoordinates, + @Nullable List repositories) { + super(session, trace); + this.artifactCoordinates = artifactCoordinates; + this.repositories = repositories; + } + + @Nonnull + @Override + public ArtifactCoordinates getArtifactCoordinates() { + return artifactCoordinates; + } + + @Nullable + @Override + public List getRepositories() { + return repositories; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultVersionResolverRequest that + && Objects.equals(artifactCoordinates, that.artifactCoordinates) + && Objects.equals(repositories, that.repositories); + } + + @Override + public int hashCode() { + return Objects.hash(artifactCoordinates, repositories); + } + + @Override + public String toString() { + return "VersionResolverRequest[" + "artifactCoordinates=" + + artifactCoordinates + ", repositories=" + + repositories + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java new file mode 100644 index 000000000000..798190c0a08e --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.Optional; + +import org.apache.maven.api.Repository; +import org.apache.maven.api.Version; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Represents the result of a version range resolution request. This interface provides access to + * information about resolved versions that match a version range constraint, including any exceptions + * that occurred during resolution, the available versions, and their source repositories. + * + *

The versions returned by this interface are guaranteed to be in ascending order.

+ * + * @since 4.0.0 + */ +@Experimental +public interface VersionRangeResolverResult extends Result { + + /** + * Gets the exceptions that occurred while resolving the version range. + * + * @return The list of exceptions that occurred during resolution, never {@code null} + */ + @Nonnull + List getExceptions(); + + /** + * Gets the versions (in ascending order) that matched the requested range. + * + * @return The list of matching versions, never {@code null}. An empty list indicates + * no versions matched the requested range. + */ + @Nonnull + List getVersions(); + + /** + * Gets the lowest version matching the requested range. + * + * @return An Optional containing the lowest matching version, or empty Optional if no versions + * matched the requested range + */ + @Nonnull + default Optional getLowestVersion() { + return getVersions().isEmpty() + ? Optional.empty() + : Optional.of(getVersions().get(0)); + } + + /** + * Gets the highest version matching the requested range. + * + * @return An Optional containing the highest matching version, or empty Optional if no versions + * matched the requested range + */ + @Nonnull + default Optional getHighestVersion() { + return getVersions().isEmpty() + ? Optional.empty() + : Optional.of(getVersions().get(getVersions().size() - 1)); + } + + /** + * Gets the repository from which the specified version was resolved. + * + * @param version The version whose source repository should be retrieved, must not be {@code null} + * @return An Optional containing the repository from which the version was resolved, + * or empty Optional if the repository is unknown + */ + @Nonnull + Optional getRepository(Version version); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolver.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolver.java new file mode 100644 index 000000000000..9f1e23d1c1d3 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolver.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Resolves artifact meta/pseudo versions. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface VersionResolver extends Service { + + /** + * Resolves an artifact's meta version (if any) to a concrete version. + * For example, resolves "1.0-SNAPSHOT" to "1.0-20090208.132618-23". + * + * @param session The repository session, must not be {@code null}. + * @param artifactCoordinates The artifact coordinates for which the version needs to be resolved, must not be {@code null} + * @return The version result, never {@code null}. + * @throws VersionResolverException If the metaversion could not be resolved. + */ + @Nonnull + default VersionResolverResult resolve(@Nonnull Session session, @Nonnull ArtifactCoordinates artifactCoordinates) + throws VersionResolverException { + return resolve(VersionResolverRequest.build(session, artifactCoordinates)); + } + + /** + * Resolves an artifact's meta version (if any) to a concrete version. + * For example, resolves "1.0-SNAPSHOT" to "1.0-20090208.132618-23". + * + * @param request The version request, must not be {@code null}. + * @return The version result, never {@code null}. + * @throws VersionResolverException If the metaversion could not be resolved. + */ + @Nonnull + VersionResolverResult resolve(@Nonnull VersionResolverRequest request) throws VersionResolverException; +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java new file mode 100644 index 000000000000..3b650a135c6a --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.Serial; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; + +/** + * @since 4.0.0 + */ +@Experimental +@Consumer +public class VersionResolverException extends MavenException { + + @Serial + private static final long serialVersionUID = -2105433586719466573L; + + public VersionResolverException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java new file mode 100644 index 000000000000..c8dee58a8fcf --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.Objects; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface VersionResolverRequest extends Request { + + @Nonnull + ArtifactCoordinates getArtifactCoordinates(); + + @Nullable + List getRepositories(); + + @Nonnull + static VersionResolverRequest build(@Nonnull Session session, @Nonnull ArtifactCoordinates artifactCoordinates) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .artifactCoordinates(requireNonNull(artifactCoordinates, "artifactCoordinates cannot be null")) + .build(); + } + + @Nonnull + static VersionResolverRequest build( + @Nonnull Session session, + @Nonnull ArtifactCoordinates artifactCoordinates, + @Nullable List repositories) { + return builder() + .session(requireNonNull(session, "session cannot be null")) + .artifactCoordinates(requireNonNull(artifactCoordinates, "artifactCoordinates cannot be null")) + .repositories(repositories) + .build(); + } + + @Nonnull + static VersionResolverRequestBuilder builder() { + return new VersionResolverRequestBuilder(); + } + + @NotThreadSafe + class VersionResolverRequestBuilder { + Session session; + RequestTrace trace; + ArtifactCoordinates artifactCoordinates; + List repositories; + + public VersionResolverRequestBuilder session(Session session) { + this.session = session; + return this; + } + + public VersionResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + + public VersionResolverRequestBuilder artifactCoordinates(ArtifactCoordinates artifactCoordinates) { + this.artifactCoordinates = artifactCoordinates; + return this; + } + + public VersionResolverRequestBuilder repositories(List repositories) { + this.repositories = repositories; + return this; + } + + public VersionResolverRequest build() { + return new DefaultVersionResolverRequest(session, trace, artifactCoordinates, repositories); + } + + private static class DefaultVersionResolverRequest extends BaseRequest + implements VersionResolverRequest { + private final ArtifactCoordinates artifactCoordinates; + private final List repositories; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultVersionResolverRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull ArtifactCoordinates artifactCoordinates, + @Nullable List repositories) { + super(session, trace); + this.artifactCoordinates = artifactCoordinates; + this.repositories = repositories; + } + + @Nonnull + @Override + public ArtifactCoordinates getArtifactCoordinates() { + return artifactCoordinates; + } + + @Nullable + @Override + public List getRepositories() { + return repositories; + } + + @Override + public boolean equals(Object o) { + return o instanceof DefaultVersionResolverRequest that + && Objects.equals(artifactCoordinates, that.artifactCoordinates) + && Objects.equals(repositories, that.repositories); + } + + @Override + public int hashCode() { + return Objects.hash(artifactCoordinates, repositories); + } + + @Override + public String toString() { + return "VersionResolverRequest[" + "artifactCoordinates=" + + artifactCoordinates + ", repositories=" + + repositories + ']'; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java new file mode 100644 index 000000000000..5b9e61b40d01 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.List; +import java.util.Optional; + +import org.apache.maven.api.Repository; +import org.apache.maven.api.Version; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * + * @since 4.0.0 + */ +@Experimental +public interface VersionResolverResult extends Result { + + @Nonnull + List getExceptions(); + + @Nonnull + Version getVersion(); + + @Nonnull + Optional getRepository(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/package-info.java new file mode 100644 index 000000000000..228ba1c49076 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Defines core service interfaces that provide essential Maven functionality such as + * artifact resolution, dependency management, and build execution. These services + * form the backbone of Maven's extensible architecture. + * + * @since 4.0.0 + */ +package org.apache.maven.api.services; diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/Location.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/Location.java new file mode 100644 index 000000000000..a79d9fcb8d62 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/Location.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +public interface Location { + + /** + * Return the line number where the current event ends, + * returns -1 if none is available. + * @return the current line number + */ + int getLineNumber(); + + /** + * Return the column number where the current event ends, + * returns -1 if none is available. + * @return the current column number + */ + int getColumnNumber(); + + /** + * Return the byte or character offset into the input source this location + * is pointing to. If the input source is a file or a byte stream then + * this is the byte offset into that stream, but if the input source is + * a character media then the offset is the character offset. + * Returns -1 if there is no offset available. + * @return the current offset + */ + int getCharacterOffset(); + + /** + * Returns the public ID of the XML + * @return the public ID, or null if not available + */ + String getPublicId(); + + /** + * Returns the system ID of the XML + * @return the system ID, or null if not available + */ + String getSystemId(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/ModelXmlFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/ModelXmlFactory.java new file mode 100644 index 000000000000..f02e4e5ab142 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/ModelXmlFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.model.Model; + +/** + * Reads or writes a {@link Model} using XML. + * + * @since 4.0.0 + */ +@Experimental +public interface ModelXmlFactory extends XmlFactory {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/PluginXmlFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/PluginXmlFactory.java new file mode 100644 index 000000000000..36b089723cec --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/PluginXmlFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.plugin.descriptor.PluginDescriptor; + +/** + * Reads and writes a {@link PluginDescriptor} object to/from XML. + * + * @since 4.0.0 + */ +@Experimental +public interface PluginXmlFactory extends XmlFactory {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/SettingsXmlFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/SettingsXmlFactory.java new file mode 100644 index 000000000000..9010dfa03b50 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/SettingsXmlFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.settings.Settings; + +/** + * Reads and writes a {@link Settings} object to/from XML. + * + * @since 4.0.0 + */ +@Experimental +public interface SettingsXmlFactory extends XmlFactory {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/ToolchainsXmlFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/ToolchainsXmlFactory.java new file mode 100644 index 000000000000..ec90299decc6 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/ToolchainsXmlFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.toolchain.PersistedToolchains; + +/** + * Reads and writes a {@link PersistedToolchains} object to/from XML. + * + * @since 4.0.0 + */ +@Experimental +public interface ToolchainsXmlFactory extends XmlFactory {} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlFactory.java new file mode 100644 index 000000000000..733e70b282d1 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlFactory.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.file.Path; + +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Generic interface to read/write objects to/from XML. + * + * @param the object type to read/write + * @since 4.0.0 + */ +@Experimental +public interface XmlFactory extends Service { + + @Nonnull + default T read(@Nonnull Path path) throws XmlReaderException { + return read(path, true); + } + + @Nonnull + default T read(@Nonnull Path path, boolean strict) throws XmlReaderException { + return read(XmlReaderRequest.builder().path(path).strict(strict).build()); + } + + @Nonnull + default T read(@Nonnull InputStream input) throws XmlReaderException { + return read(input, true); + } + + @Nonnull + default T read(@Nonnull InputStream input, boolean strict) throws XmlReaderException { + return read(XmlReaderRequest.builder().inputStream(input).strict(strict).build()); + } + + @Nonnull + default T read(@Nonnull Reader reader) throws XmlReaderException { + return read(reader, true); + } + + @Nonnull + default T read(@Nonnull Reader reader, boolean strict) throws XmlReaderException { + return read(XmlReaderRequest.builder().reader(reader).strict(strict).build()); + } + + @Nonnull + T read(@Nonnull XmlReaderRequest request) throws XmlReaderException; + + default void write(@Nonnull T content, @Nonnull Path path) throws XmlWriterException { + write(XmlWriterRequest.builder().content(content).path(path).build()); + } + + default void write(@Nonnull T content, @Nonnull OutputStream outputStream) throws XmlWriterException { + write(XmlWriterRequest.builder() + .content(content) + .outputStream(outputStream) + .build()); + } + + default void write(@Nonnull T content, @Nonnull Writer writer) throws XmlWriterException { + write(XmlWriterRequest.builder().content(content).writer(writer).build()); + } + + void write(@Nonnull XmlWriterRequest request) throws XmlWriterException; + + /** + * Simply parse the given xml string. + * + * @param xml the input xml string + * @return the parsed object + * @throws XmlReaderException if an error occurs during the parsing + * @see #toXmlString(Object) + */ + @Nonnull + default T fromXmlString(@Nonnull String xml) throws XmlReaderException { + return read(new StringReader(xml)); + } + + /** + * Converts the given content to an XML string. + * + * @param content the object to convert + * @return the xml string representation + * @throws XmlWriterException if an error occurs during the transformation + * @see #fromXmlString(String) + */ + @Nonnull + default String toXmlString(@Nonnull T content) throws XmlWriterException { + StringWriter sw = new StringWriter(); + write(content, sw); + return sw.toString(); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlReaderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlReaderException.java new file mode 100644 index 000000000000..f6317521590d --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlReaderException.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.MavenException; + +/** + * An exception thrown while reading an XML file. + * + * @since 4.0.0 + */ +@Experimental +public class XmlReaderException extends MavenException { + + private final Location location; + + /** + * @param message the message for the exception + * @param e the exception itself + */ + public XmlReaderException(String message, Location location, Exception e) { + super(message, e); + this.location = location; + } + + public Location getLocation() { + return location; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlReaderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlReaderRequest.java new file mode 100644 index 000000000000..d6fc50e911ad --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlReaderRequest.java @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import java.io.InputStream; +import java.io.Reader; +import java.net.URL; +import java.nio.file.Path; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +/** + * An XML reader request. + * + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface XmlReaderRequest { + + @Nullable + Path getPath(); + + @Nullable + Path getRootDirectory(); + + @Nullable + URL getURL(); + + @Nullable + InputStream getInputStream(); + + @Nullable + Reader getReader(); + + @Nullable + Transformer getTransformer(); + + boolean isStrict(); + + @Nullable + String getModelId(); + + @Nullable + String getLocation(); + + boolean isAddDefaultEntities(); + + interface Transformer { + /** + * Interpolate the value read from the xml document + * + * @param source The source value + * @param fieldName A description of the field being interpolated. The implementation may use this to + * log stuff. + * @return the interpolated value + */ + String transform(String source, String fieldName); + } + + @Nonnull + static XmlReaderRequestBuilder builder() { + return new XmlReaderRequestBuilder(); + } + + @NotThreadSafe + class XmlReaderRequestBuilder { + Path path; + Path rootDirectory; + URL url; + InputStream inputStream; + Reader reader; + Transformer transformer; + boolean strict; + String modelId; + String location; + boolean addDefaultEntities = true; + + public XmlReaderRequestBuilder path(Path path) { + this.path = path; + return this; + } + + public XmlReaderRequestBuilder rootDirectory(Path rootDirectory) { + this.rootDirectory = rootDirectory; + return this; + } + + public XmlReaderRequestBuilder url(URL url) { + this.url = url; + return this; + } + + public XmlReaderRequestBuilder inputStream(InputStream inputStream) { + this.inputStream = inputStream; + return this; + } + + public XmlReaderRequestBuilder reader(Reader reader) { + this.reader = reader; + return this; + } + + public XmlReaderRequestBuilder transformer(Transformer transformer) { + this.transformer = transformer; + return this; + } + + public XmlReaderRequestBuilder strict(boolean strict) { + this.strict = strict; + return this; + } + + public XmlReaderRequestBuilder modelId(String modelId) { + this.modelId = modelId; + return this; + } + + public XmlReaderRequestBuilder location(String location) { + this.location = location; + return this; + } + + public XmlReaderRequestBuilder addDefaultEntities(boolean addDefaultEntities) { + this.addDefaultEntities = addDefaultEntities; + return this; + } + + public XmlReaderRequest build() { + return new DefaultXmlReaderRequest( + path, + rootDirectory, + url, + inputStream, + reader, + transformer, + strict, + modelId, + location, + addDefaultEntities); + } + + private static class DefaultXmlReaderRequest implements XmlReaderRequest { + final Path path; + final Path rootDirectory; + final URL url; + final InputStream inputStream; + final Reader reader; + final Transformer transformer; + final boolean strict; + final String modelId; + final String location; + final boolean addDefaultEntities; + + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultXmlReaderRequest( + Path path, + Path rootDirectory, + URL url, + InputStream inputStream, + Reader reader, + Transformer transformer, + boolean strict, + String modelId, + String location, + boolean addDefaultEntities) { + this.path = path; + this.rootDirectory = rootDirectory; + this.url = url; + this.inputStream = inputStream; + this.reader = reader; + this.transformer = transformer; + this.strict = strict; + this.modelId = modelId; + this.location = location; + this.addDefaultEntities = addDefaultEntities; + } + + @Override + public Path getPath() { + return path; + } + + @Override + public Path getRootDirectory() { + return rootDirectory; + } + + @Override + public URL getURL() { + return null; + } + + @Override + public InputStream getInputStream() { + return inputStream; + } + + @Override + public Reader getReader() { + return reader; + } + + @Override + public Transformer getTransformer() { + return transformer; + } + + @Override + public boolean isStrict() { + return strict; + } + + @Override + public String getModelId() { + return modelId; + } + + @Override + public String getLocation() { + return location; + } + + @Override + public boolean isAddDefaultEntities() { + return addDefaultEntities; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlWriterException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlWriterException.java new file mode 100644 index 000000000000..69852d375fde --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlWriterException.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.MavenException; + +/** + * An exception thrown while writing an XML file. + * + * @since 4.0.0 + */ +@Experimental +public class XmlWriterException extends MavenException { + + private final Location location; + + /** + * @param message the message for the exception + * @param e the exception itself + */ + public XmlWriterException(String message, Location location, Exception e) { + super(message, e); + this.location = location; + } + + public Location getLocation() { + return location; + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlWriterRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlWriterRequest.java new file mode 100644 index 000000000000..46ee89c8cf49 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/XmlWriterRequest.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services.xml; + +import java.io.OutputStream; +import java.io.Writer; +import java.nio.file.Path; +import java.util.function.Function; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * An XML writer request. + * + * @since 4.0.0 + * @param the object type to read + */ +@Experimental +public interface XmlWriterRequest { + + @Nullable + Path getPath(); + + @Nullable + OutputStream getOutputStream(); + + @Nullable + Writer getWriter(); + + @Nonnull + T getContent(); + + @Nullable + Function getInputLocationFormatter(); + + static XmlWriterRequestBuilder builder() { + return new XmlWriterRequestBuilder<>(); + } + + class XmlWriterRequestBuilder { + Path path; + OutputStream outputStream; + Writer writer; + T content; + Function inputLocationFormatter; + + public XmlWriterRequestBuilder path(Path path) { + this.path = path; + return this; + } + + public XmlWriterRequestBuilder outputStream(OutputStream outputStream) { + this.outputStream = outputStream; + return this; + } + + public XmlWriterRequestBuilder writer(Writer writer) { + this.writer = writer; + return this; + } + + public XmlWriterRequestBuilder content(T content) { + this.content = content; + return this; + } + + public XmlWriterRequestBuilder inputLocationFormatter(Function inputLocationFormatter) { + this.inputLocationFormatter = inputLocationFormatter; + return this; + } + + public XmlWriterRequest build() { + return new DefaultXmlWriterRequest<>(path, outputStream, writer, content, inputLocationFormatter); + } + + private static class DefaultXmlWriterRequest implements XmlWriterRequest { + final Path path; + final OutputStream outputStream; + final Writer writer; + final T content; + final Function inputLocationFormatter; + + DefaultXmlWriterRequest( + Path path, + OutputStream outputStream, + Writer writer, + T content, + Function inputLocationFormatter) { + this.path = path; + this.outputStream = outputStream; + this.writer = writer; + this.content = content; + this.inputLocationFormatter = inputLocationFormatter; + } + + @Override + public Path getPath() { + return path; + } + + @Override + public OutputStream getOutputStream() { + return outputStream; + } + + @Override + public Writer getWriter() { + return writer; + } + + @Override + public T getContent() { + return content; + } + + @Override + public Function getInputLocationFormatter() { + return inputLocationFormatter; + } + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/package-info.java new file mode 100644 index 000000000000..f3aea327ef20 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/xml/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides XML-specific services for reading and writing Maven's configuration files + * and descriptors. These services handle XML parsing, transformation, and serialization + * while maintaining Maven's model integrity. + * + * @since 4.0.0 + */ +package org.apache.maven.api.services.xml; diff --git a/api/maven-api-core/src/site/site.xml b/api/maven-api-core/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-core/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/JavaPathTypeTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/JavaPathTypeTest.java new file mode 100644 index 000000000000..e46ca80a269d --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/JavaPathTypeTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class JavaPathTypeTest { + /** + * {@return dummy paths to use in tests}. + */ + private static List paths() { + return List.of(Path.of("src", "foo.java"), Path.of("src", "bar.java")); + } + + /** + * Converts paths from Unix style to platform-dependent style. + * + * @param expected the option value expected by the test + * @return the expected value with separators of the host + */ + private static String toPlatformSpecific(String expected) { + return expected.replace("/", File.separator).replace(":", File.pathSeparator); + } + + /** + * Tests the formatting of an option without module name. + */ + @Test + public void testOption() { + String[] formatted = JavaPathType.MODULES.option(paths()); + assertEquals(2, formatted.length); + assertEquals("--module-path", formatted[0]); + assertEquals(toPlatformSpecific("\"src/foo.java:src/bar.java\""), formatted[1]); + } + + /** + * Tests the formatting of an option with a module name. + */ + @Test + public void testModularOption() { + String[] formatted = JavaPathType.patchModule("my.module").option(paths()); + assertEquals(2, formatted.length); + assertEquals("--patch-module", formatted[0]); + assertEquals(toPlatformSpecific("my.module=\"src/foo.java:src/bar.java\""), formatted[1]); + } +} diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/MonotonicClockTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/MonotonicClockTest.java new file mode 100644 index 000000000000..de561fb591a4 --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/MonotonicClockTest.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class MonotonicClockTest { + + @Test + @DisplayName("MonotonicClock singleton instance should always return the same instance") + void testSingletonInstance() { + MonotonicClock clock1 = MonotonicClock.get(); + MonotonicClock clock2 = MonotonicClock.get(); + + assertSame(clock1, clock2, "Multiple calls to get() should return the same instance"); + } + + @Test + @DisplayName("MonotonicClock should always use UTC timezone") + void testClockTimezone() { + MonotonicClock clock = MonotonicClock.get(); + + assertEquals(ZoneOffset.UTC, clock.getZone(), "Clock should use UTC timezone"); + + // Verify that attempting to change timezone returns the same instance + Clock newClock = clock.withZone(ZoneId.systemDefault()); + assertSame(clock, newClock, "withZone() should return the same clock instance"); + } + + @Test + @DisplayName("MonotonicClock should maintain monotonic time progression") + void testMonotonicBehavior() throws InterruptedException { + Instant first = MonotonicClock.now(); + Thread.sleep(10); // Small delay + Instant second = MonotonicClock.now(); + Thread.sleep(10); // Small delay + Instant third = MonotonicClock.now(); + + assertTrue(first.isBefore(second), "Time should progress forward between measurements"); + assertTrue(second.isBefore(third), "Time should progress forward between measurements"); + } + + @Test + @DisplayName("MonotonicClock elapsed time should increase") + void testElapsedTime() throws InterruptedException { + Duration initial = MonotonicClock.elapsed(); + Thread.sleep(50); // Longer delay for more reliable measurement + Duration later = MonotonicClock.elapsed(); + + assertTrue(later.compareTo(initial) > 0, "Elapsed time should increase"); + assertTrue( + later.minus(initial).toMillis() >= 45, + "Elapsed time difference should be at least 45ms (accounting for some timing variance)"); + } + + @Test + @DisplayName("MonotonicClock start time should remain constant") + void testStartTime() throws InterruptedException { + Instant start1 = MonotonicClock.start(); + Thread.sleep(10); + Instant start2 = MonotonicClock.start(); + + assertEquals(start1, start2, "Start time should remain constant"); + assertNotNull(start1, "Start time should not be null"); + } + + @Nested + @DisplayName("Time consistency tests") + class TimeConsistencyTests { + + @Test + @DisplayName("Current time should be after start time") + void testCurrentTimeAfterStart() { + Instant now = MonotonicClock.now(); + Instant start = MonotonicClock.start(); + + assertTrue(now.isAfter(start), "Current time should be after start time"); + } + + @Test + @DisplayName("Elapsed time should match time difference") + void testElapsedTimeConsistency() { + MonotonicClock clock = MonotonicClock.get(); + Instant now = clock.instant(); + Duration elapsed = clock.elapsedTime(); + Duration calculated = Duration.between(clock.startInstant(), now); + + // Allow for small timing differences (1ms) due to execution time between measurements + assertTrue( + Math.abs(elapsed.toMillis() - calculated.toMillis()) <= 1, + "Elapsed time should match calculated duration between start and now"); + } + } + + @Test + @DisplayName("MonotonicClock should handle rapid successive calls") + void testRapidCalls() { + Instant[] instants = new Instant[1000]; + for (int i = 0; i < instants.length; i++) { + instants[i] = MonotonicClock.now(); + } + + // Verify monotonic behavior across all measurements + for (int i = 1; i < instants.length; i++) { + assertTrue( + instants[i].compareTo(instants[i - 1]) >= 0, + "Time should never go backwards even with rapid successive calls"); + } + } + + @Test + @DisplayName("MonotonicClock should maintain reasonable alignment with system time") + void testSystemTimeAlignment() { + Instant monotonic = MonotonicClock.now(); + Instant system = Instant.now(); + + // The difference should be relatively small (allow for 1 second max) + Duration difference = Duration.between(monotonic, system).abs(); + assertTrue(difference.getSeconds() <= 1, "Monotonic time should be reasonably aligned with system time"); + } +} diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/feature/FeaturesTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/feature/FeaturesTest.java new file mode 100644 index 000000000000..0b938b9df3b7 --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/feature/FeaturesTest.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.feature; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.api.Constants; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the Features class. + */ +class FeaturesTest { + + @Test + void testDeployBuildPomDefaultValue() { + // Test that deployBuildPom returns true by default (when property is not set) + Map emptyProperties = Map.of(); + assertTrue(Features.deployBuildPom(emptyProperties)); + + // Test with null properties + assertTrue(Features.deployBuildPom(null)); + } + + @Test + void testDeployBuildPomWithStringTrue() { + // Test with string "true" + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "true"); + assertTrue(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithStringFalse() { + // Test with string "false" + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithBooleanTrue() { + // Test with Boolean.TRUE + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, Boolean.TRUE); + assertTrue(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithBooleanFalse() { + // Test with Boolean.FALSE + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, Boolean.FALSE); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithStringTrueUpperCase() { + // Test case-insensitive string parsing - TRUE + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "TRUE"); + assertTrue(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithStringFalseUpperCase() { + // Test case-insensitive string parsing - FALSE + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "FALSE"); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithStringTrueMixedCase() { + // Test case-insensitive string parsing - True + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "True"); + assertTrue(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithStringFalseMixedCase() { + // Test case-insensitive string parsing - False + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "False"); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithInvalidStringValue() { + // Test that invalid string values default to false (Boolean.parseBoolean behavior) + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "invalid"); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithEmptyString() { + // Test that empty string defaults to false + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, ""); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithYesString() { + // Test that "yes" string defaults to false (not a valid boolean) + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "yes"); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithNumericString() { + // Test that numeric string defaults to false + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "1"); + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testDeployBuildPomWithIntegerOne() { + // Test with integer 1 (should use toString() and then parseBoolean) + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, 1); + assertFalse(Features.deployBuildPom(properties)); // "1".parseBoolean() = false + } + + @Test + void testDeployBuildPomWithIntegerZero() { + // Test with integer 0 (should use toString() and then parseBoolean) + Map properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, 0); + assertFalse(Features.deployBuildPom(properties)); // "0".parseBoolean() = false + } + + @Test + void testDeployBuildPomWithMutableMap() { + // Test with a mutable map to ensure the method doesn't modify the input + Map properties = new HashMap<>(); + properties.put(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); + + assertFalse(Features.deployBuildPom(properties)); + + // Verify the map wasn't modified + assertEquals(1, properties.size()); + assertEquals("false", properties.get(Constants.MAVEN_DEPLOY_BUILD_POM)); + } + + @Test + void testDeployBuildPomWithOtherProperties() { + // Test that other properties don't interfere + Map properties = Map.of( + Constants.MAVEN_CONSUMER_POM, + "false", + Constants.MAVEN_MAVEN3_PERSONALITY, + "true", + "some.other.property", + "value", + Constants.MAVEN_DEPLOY_BUILD_POM, + "false"); + + assertFalse(Features.deployBuildPom(properties)); + } + + @Test + void testConsistencyWithOtherFeatureMethodsFalse() { + // Test that deployBuildPom behaves consistently with other feature methods when false + Map properties = Map.of( + Constants.MAVEN_DEPLOY_BUILD_POM, "false", + Constants.MAVEN_CONSUMER_POM, "false"); + + assertFalse(Features.deployBuildPom(properties)); + assertFalse(Features.consumerPom(properties)); + } + + @Test + void testConsistencyWithOtherFeatureMethodsTrue() { + // Test that deployBuildPom behaves consistently with other feature methods when true + Map properties = Map.of( + Constants.MAVEN_DEPLOY_BUILD_POM, "true", + Constants.MAVEN_CONSUMER_POM, "true"); + + assertTrue(Features.deployBuildPom(properties)); + assertTrue(Features.consumerPom(properties)); + } +} diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java new file mode 100644 index 000000000000..eef64a138888 --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.PathScope; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +class RequestImplementationTest { + + @Test + void testArtifactResolverRequestEquality() { + Session session = mock(Session.class); + ArtifactCoordinates coords1 = mock(ArtifactCoordinates.class); + ArtifactCoordinates coords2 = mock(ArtifactCoordinates.class); + RemoteRepository repo1 = mock(RemoteRepository.class); + RemoteRepository repo2 = mock(RemoteRepository.class); + + List repositories1 = Arrays.asList(repo1, repo2); + List repositories2 = Arrays.asList(repo1, repo2); + + ArtifactResolverRequest.ArtifactResolverRequestBuilder builder = ArtifactResolverRequest.builder(); + + ArtifactResolverRequest request1 = builder.session(session) + .coordinates(Arrays.asList(coords1, coords2)) + .repositories(repositories1) + .build(); + + ArtifactResolverRequest request2 = builder.session(session) + .coordinates(Arrays.asList(coords1, coords2)) + .repositories(repositories2) + .build(); + + ArtifactResolverRequest request3 = builder.session(session) + .coordinates(Arrays.asList(coords2, coords1)) // Different order + .repositories(repositories1) + .build(); + + // Test equals and hashCode + assertEquals(request1, request2); + assertEquals(request1.hashCode(), request2.hashCode()); + assertNotEquals(request1, request3); + + // Test toString + String toString = request1.toString(); + assertTrue(toString.contains("coordinates=")); + assertTrue(toString.contains("repositories=")); + } + + @Test + void testRequestTraceIntegration() { + Session session = mock(Session.class); + RequestTrace trace = new RequestTrace("test-context", null, "test-data"); + + ArtifactInstallerRequest request = + ArtifactInstallerRequest.builder().session(session).trace(trace).build(); + + assertEquals(trace, request.getTrace()); + assertEquals(session, request.getSession()); + } + + @Test + void testDependencyResolverRequestEquality() { + Session session = mock(Session.class); + + DependencyResolverRequest.DependencyResolverRequestBuilder builder = DependencyResolverRequest.builder(); + DependencyResolverRequest request1 = builder.session(session) + .requestType(DependencyResolverRequest.RequestType.COLLECT) + .pathScope(PathScope.MAIN_COMPILE) + .build(); + + DependencyResolverRequest request2 = builder.session(session) + .requestType(DependencyResolverRequest.RequestType.COLLECT) + .pathScope(PathScope.MAIN_COMPILE) + .build(); + + DependencyResolverRequest request3 = builder.session(session) + .requestType(DependencyResolverRequest.RequestType.RESOLVE) + .pathScope(PathScope.MAIN_COMPILE) + .build(); + + assertEquals(request1, request2); + assertEquals(request1.hashCode(), request2.hashCode()); + assertNotEquals(request1, request3); + + String toString = request1.toString(); + assertTrue(toString.contains("requestType=")); + assertTrue(toString.contains("pathScope=")); + } +} diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java new file mode 100644 index 000000000000..78534f9ae5f5 --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +class RequestTraceTest { + + @Test + void testRequestTraceCreation() { + RequestTrace parentTrace = new RequestTrace("parent-context", null, "parent-data"); + RequestTrace childTrace = new RequestTrace("child-context", parentTrace, "child-data"); + + assertEquals("parent-context", parentTrace.context()); + assertNull(parentTrace.parent()); + assertEquals("parent-data", parentTrace.data()); + + assertEquals("child-context", childTrace.context()); + assertSame(parentTrace, childTrace.parent()); + assertEquals("child-data", childTrace.data()); + } + + @Test + void testRequestTraceWithParentContextInheritance() { + RequestTrace parentTrace = new RequestTrace("parent-context", null, "parent-data"); + RequestTrace childTrace = new RequestTrace(parentTrace, "child-data"); + + assertEquals("parent-context", parentTrace.context()); + assertEquals("parent-context", childTrace.context()); + assertEquals("child-data", childTrace.data()); + } + + @Test + void testPredefinedContexts() { + assertEquals("plugin", RequestTrace.CONTEXT_PLUGIN); + assertEquals("project", RequestTrace.CONTEXT_PROJECT); + assertEquals("bootstrap", RequestTrace.CONTEXT_BOOTSTRAP); + } + + @Test + void testNullValues() { + RequestTrace trace = new RequestTrace(null, null, null); + assertNull(trace.context()); + assertNull(trace.parent()); + assertNull(trace.data()); + } + + @Test + void testChainedTraces() { + RequestTrace root = new RequestTrace("root", null, "root-data"); + RequestTrace level1 = new RequestTrace("level1", root, "level1-data"); + RequestTrace level2 = new RequestTrace("level2", level1, "level2-data"); + RequestTrace level3 = new RequestTrace(level2, "level3-data"); + + // Verify the chain + assertNull(root.parent()); + assertEquals(root, level1.parent()); + assertEquals(level1, level2.parent()); + assertEquals(level2, level3.parent()); + + // Verify context inheritance + assertEquals("root", root.context()); + assertEquals("level1", level1.context()); + assertEquals("level2", level2.context()); + assertEquals("level2", level3.context()); // Inherited from parent + } +} diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java new file mode 100644 index 000000000000..f9abbe66d28b --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.services; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class SourcesTest { + @TempDir + Path tempDir; + + @Test + void testFromPath() { + Path path = Paths.get("/tmp"); + Source source = Sources.fromPath(path); + + assertNotNull(source); + assertInstanceOf(Sources.PathSource.class, source); + assertEquals(path.normalize(), source.getPath()); + } + + @Test + void testBuildSource() { + Path path = Paths.get("/tmp"); + ModelSource source = Sources.buildSource(path); + + assertNotNull(source); + assertInstanceOf(Sources.BuildPathSource.class, source); + assertEquals(path.normalize(), source.getPath()); + } + + @Test + void testResolvedSource() { + Path path = Paths.get("/tmp"); + String location = "custom-location"; + ModelSource source = Sources.resolvedSource(path, location); + + assertNotNull(source); + assertInstanceOf(Sources.ResolvedPathSource.class, source); + assertNull(source.getPath()); + assertEquals(location, source.getLocation()); + } + + @Test + void testPathSourceFunctionality() { + // Test basic source functionality + Path path = Paths.get("/tmp"); + Sources.PathSource source = (Sources.PathSource) Sources.fromPath(path); + + assertEquals(path.normalize(), source.getPath()); + assertEquals(path.toString(), source.getLocation()); + + Source resolved = source.resolve("subdir"); + assertNotNull(resolved); + assertEquals(path.resolve("subdir").normalize(), resolved.getPath()); + } + + @Test + void testBuildPathSourceFunctionality() { + // Test build source functionality + Path basePath = Paths.get("/tmp"); + ModelSource.ModelLocator locator = mock(ModelSource.ModelLocator.class); + Path resolvedPath = Paths.get("/tmp/subproject/pom.xml"); + when(locator.locateExistingPom(any(Path.class))).thenReturn(resolvedPath); + + Sources.BuildPathSource source = (Sources.BuildPathSource) Sources.buildSource(basePath); + ModelSource resolved = source.resolve(locator, "subproject"); + + assertNotNull(resolved); + assertInstanceOf(Sources.BuildPathSource.class, resolved); + assertEquals(resolvedPath, resolved.getPath()); + + verify(locator).locateExistingPom(any(Path.class)); + } + + @Test + void testResolvedPathSourceFunctionality() { + // Test resolved source functionality + Path path = Paths.get("/tmp"); + String location = "custom-location"; + Sources.ResolvedPathSource source = (Sources.ResolvedPathSource) Sources.resolvedSource(path, location); + + assertNull(source.getPath()); + assertEquals(location, source.getLocation()); + assertNull(source.resolve("subdir")); + + ModelSource.ModelLocator locator = mock(ModelSource.ModelLocator.class); + assertNull(source.resolve(locator, "subproject")); + verify(locator, never()).locateExistingPom(any(Path.class)); + } + + @Test + void testStreamReading() throws IOException { + // Test stream reading functionality + Path testFile = tempDir.resolve("test.txt"); + String content = "test content"; + Files.writeString(testFile, content); + + Source source = Sources.fromPath(testFile); + try (InputStream inputStream = source.openStream()) { + String readContent = new String(inputStream.readAllBytes()); + assertEquals(content, readContent); + } + } + + @Test + void testNullHandling() { + assertThrows(NullPointerException.class, () -> Sources.fromPath(null)); + assertThrows(NullPointerException.class, () -> Sources.buildSource(null)); + assertThrows(NullPointerException.class, () -> Sources.resolvedSource(null, "location")); + } +} diff --git a/api/maven-api-di/pom.xml b/api/maven-api-di/pom.xml new file mode 100644 index 000000000000..d2341a7a42b5 --- /dev/null +++ b/api/maven-api-di/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-di + Maven 4 API :: Dependency Injection + Maven 4 API - Dependency Injection + + + none + + diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Inject.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Inject.java new file mode 100644 index 000000000000..eb02bb38331b --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Inject.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks a dependency injection point for constructor, method, or field injection. + *

+ * This annotation is used to identify injection points where the container should + * provide an instance of the requested type. It can be applied to: + *

    + *
  • Constructors
  • + *
  • Methods
  • + *
  • Fields
  • + *
+ *

+ * Example usage: + *

+ * public class MyService {
+ *     private final Repository repository;
+ *
+ *     {@literal @}Inject
+ *     public MyService(Repository repository) {
+ *         this.repository = repository;
+ *     }
+ * }
+ * 
+ * + * @see Named + * @since 4.0.0 + */ +@Target({FIELD, CONSTRUCTOR, METHOD}) +@Retention(RUNTIME) +@Documented +public @interface Inject {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java new file mode 100644 index 000000000000..729ec5e10780 --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates that the annotated bean has a lifespan limited to a given mojo execution, + * which means each mojo execution will result in a different instance being injected. + *

+ * The following objects will be bound to the mojo execution scope: + *

    + *
  • {@code org.apache.maven.api.MojoExecution}
  • + *
  • {@code org.apache.maven.api.Project}
  • + *
  • {@code org.apache.maven.api.plugin.Log}
  • + *
+ * + * @since 4.0.0 + */ +@Scope +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD}) +public @interface MojoExecutionScoped {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Named.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Named.java new file mode 100644 index 000000000000..fbd400f9f6b3 --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Named.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Provides a unique identifier for dependencies when multiple implementations + * of the same type are available. + *

+ * This annotation can be used in conjunction with {@link Inject} to specify + * which implementation should be injected when multiple candidates exist. + * The value represents a unique identifier for the dependency. + *

+ * Example usage: + *

+ * {@literal @}Inject
+ * {@literal @}Named("mysql")
+ * private Repository mysqlRepository;
+ * 
+ * + * @see Inject + * @see Qualifier + * @since 4.0.0 + */ +@Qualifier +@Retention(RUNTIME) +@Documented +public @interface Named { + /** + * The name identifier for the annotated element. + *

+ * If no value is specified, the default empty string will be used. + * When used as a qualifier, this value helps distinguish between different + * implementations or instances of the same type. + * + * @return the name that identifies this component + */ + String value() default ""; +} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Priority.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Priority.java new file mode 100644 index 000000000000..dd57461be31e --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Priority.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Specifies the priority of a bean implementation when multiple implementations + * of the same type are available. + *

+ * Higher values indicate higher priority. When multiple implementations of the same + * type exist, the one with the highest priority will be selected for injection. + *

+ * Example usage: + *

+ * {@literal @}Priority(100)
+ * public class PreferredImplementation implements Service {
+ *     // Implementation
+ * }
+ * 
+ * + * @since 4.0.0 + */ +@Target({TYPE, METHOD}) +@Retention(RUNTIME) +@Documented +public @interface Priority { + /** + * The priority value for the annotated element. + *

+ * Higher values indicate higher priority. When multiple implementations + * of the same type exist in the container, the one with the highest + * priority value will be selected for injection. + *

+ * There are no predefined minimum or maximum values, but it's recommended + * to use values that allow for future adjustments (e.g., using values + * like 100, 200, 300 rather than consecutive numbers). + * + * @return the priority value for ordering + */ + int value(); +} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Provides.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Provides.java new file mode 100644 index 000000000000..73bf7120596d --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Provides.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks a method as a provider of beans for dependency injection. + *

+ * This annotation can be used on static methods to programmatically create and configure + * beans that will be managed by the dependency injection container. It's particularly + * useful when the bean creation requires complex logic or when the bean needs to be + * configured based on runtime conditions. + *

+ * Example usage: + *

+ * public class Providers {
+ *     {@literal @}Provides
+ *     {@literal @}Singleton
+ *     public static Configuration provideConfiguration() {
+ *         return Configuration.load();
+ *     }
+ * }
+ * 
+ * + * @since 4.0.0 + */ +@Target(METHOD) +@Retention(RUNTIME) +@Documented +public @interface Provides {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Qualifier.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Qualifier.java new file mode 100644 index 000000000000..db778db4617e --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Qualifier.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Meta-annotation that marks other annotations as qualifier annotations. + *

+ * Qualifiers are used to distinguish between multiple beans of the same type, + * allowing for more precise control over which implementation should be injected. + * Custom qualifier annotations should be annotated with {@code @Qualifier}. + *

+ * Example of creating a custom qualifier: + *

+ * {@literal @}Qualifier
+ * {@literal @}Retention(RUNTIME)
+ * public @interface Database {
+ *     String value();
+ * }
+ * 
+ * + * @see Named + * @since 4.0.0 + */ +@Target(ANNOTATION_TYPE) +@Retention(RUNTIME) +@Documented +public @interface Qualifier {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Scope.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Scope.java new file mode 100644 index 000000000000..9468fbfe452e --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Scope.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Meta-annotation that marks other annotations as scope annotations. + *

+ * Scopes define the lifecycle and visibility of objects in the dependency injection + * system. Custom scope annotations should be annotated with {@code @Scope}. + *

+ * Built-in scopes include: + *

    + *
  • {@link Singleton} - One instance per container
  • + *
  • {@link SessionScoped} - One instance per Maven session
  • + *
  • {@link MojoExecutionScoped} - One instance per plugin execution
  • + *
+ * + * @see Singleton + * @see SessionScoped + * @see MojoExecutionScoped + * @since 4.0.0 + */ +@Target(ANNOTATION_TYPE) +@Retention(RUNTIME) +@Documented +public @interface Scope {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java new file mode 100644 index 000000000000..06c1049683ed --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates that annotated component should be instantiated before session execution starts + * and discarded after session execution completes. + *

+ * A {@code org.apache.maven.api.Session} object is available in the scope of this annotation. + * + * @since 4.0.0 + */ +@Scope +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD}) +public @interface SessionScoped {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Singleton.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Singleton.java new file mode 100644 index 000000000000..0a64845e1f55 --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Singleton.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Denotes that a bean should be created as a singleton instance. + *

+ * Singleton-scoped beans are instantiated once and reused throughout the entire + * Maven execution. This scope should be used for stateless services or components + * that can be safely shared across the entire build process. + *

+ * Example usage: + *

+ * {@literal @}Singleton
+ * public class GlobalConfiguration {
+ *     // Implementation
+ * }
+ * 
+ * + * @see Scope + * @since 4.0.0 + */ +@Scope +@Documented +@Retention(RUNTIME) +public @interface Singleton {} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Typed.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Typed.java new file mode 100644 index 000000000000..dd4a3967e551 --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Typed.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Explicitly specifies the types that should be used for dependency injection. + *

+ * This annotation allows you to limit which types of a bean should be available + * for injection. It can be used to prevent unintended automatic binding of implemented + * interfaces or extended classes. + *

+ * Example usage: + *

+ * {@literal @}Typed(ServiceImpl.class)
+ * public class ServiceImpl implements Service {
+ *     // Implementation
+ * }
+ * 
+ * + * @since 4.0.0 + */ +@Target({FIELD, METHOD, TYPE}) +@Retention(RUNTIME) +@Documented +public @interface Typed { + /** + * Specifies the types that should be considered for dependency injection. + *

+ * When specified, only the listed types will be available for injection, + * even if the class implements or extends other types. If empty, the + * default behavior is to make all supertypes available for injection. + *

+ * Example: + *

+     * {@literal @}Typed({Service.class, Monitored.class})
+     * public class ServiceImpl implements Service, Monitored, Logging {
+     *     // Only Service and Monitored will be available for injection,
+     *     // Logging interface will be ignored
+     * }
+     * 
+ * + * @return an array of classes that should be considered for injection + */ + Class[] value() default {}; +} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/package-info.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/package-info.java new file mode 100644 index 000000000000..8cda82936a36 --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/package-info.java @@ -0,0 +1,21 @@ +/** + * A dependency injection framework for Maven that provides JSR-330 style annotations + * for managing object lifecycle and dependencies within Maven's build process. + *

+ * This package provides a set of annotations that control how objects are created, + * managed and injected throughout Maven's execution lifecycle. The framework is designed + * to be lightweight yet powerful, supporting various scopes of object lifecycle from + * singleton instances to mojo-execution-scoped beans. + *

+ * Key features include: + *

    + *
  • Constructor, method, and field injection
  • + *
  • Qualifiers for distinguishing between beans of the same type
  • + *
  • Multiple scopes (Singleton, Session, and MojoExecution)
  • + *
  • Priority-based implementation selection
  • + *
  • Type-safe dependency injection
  • + *
+ * + * @since 4.0.0 + */ +package org.apache.maven.api.di; diff --git a/api/maven-api-di/src/main/java/org/apache/maven/di/tool/DiIndexProcessor.java b/api/maven-api-di/src/main/java/org/apache/maven/di/tool/DiIndexProcessor.java new file mode 100644 index 000000000000..383d2cbc9f6b --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/di/tool/DiIndexProcessor.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.di.tool; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.maven.api.di.Named; + +/** + * Annotation processor that generates an index file for classes annotated with {@link Named}. + * This processor scans for classes with the {@code @Named} annotation and creates a file + * at {@code META-INF/maven/org.apache.maven.api.di.Inject} containing the fully qualified + * names of these classes. + * + * @since 4.0.0 + */ +@SupportedAnnotationTypes("org.apache.maven.api.di.Named") +@SupportedSourceVersion(SourceVersion.RELEASE_17) +public class DiIndexProcessor extends AbstractProcessor { + + /** + * Set of fully qualified class names that have been processed and contain the {@link Named} annotation. + */ + private final Set processedClasses = new HashSet<>(); + + /** + * Processes classes with the {@link Named} annotation and generates an index file. + * + * @param annotations the annotation types requested to be processed + * @param roundEnv environment for information about the current and prior round + * @return whether or not the set of annotations are claimed by this processor + */ + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + logMessage( + Diagnostic.Kind.NOTE, "Processing " + roundEnv.getRootElements().size() + " classes"); + + for (Element element : roundEnv.getElementsAnnotatedWith(Named.class)) { + if (element instanceof TypeElement typeElement) { + String className = getFullClassName(typeElement); + processedClasses.add(className); + } + } + + if (roundEnv.processingOver()) { + try { + updateFileIfChanged(); + } catch (Exception e) { + logError("Error updating file", e); + } + } + + return true; + } + + /** + * Gets the fully qualified class name for a type element, including handling inner classes. + * + * @param typeElement the type element to get the class name for + * @return the fully qualified class name + */ + private String getFullClassName(TypeElement typeElement) { + StringBuilder className = new StringBuilder(typeElement.getSimpleName()); + Element enclosingElement = typeElement.getEnclosingElement(); + + while (enclosingElement instanceof TypeElement enclosingTypeElement) { + className.insert(0, "$").insert(0, enclosingTypeElement.getSimpleName()); + enclosingElement = enclosingElement.getEnclosingElement(); + } + + if (enclosingElement instanceof PackageElement packageElement) { + className.insert(0, ".").insert(0, packageElement.getQualifiedName()); + } + + return className.toString(); + } + + /** + * Updates the index file if its content has changed. + * The file is created at {@code META-INF/maven/org.apache.maven.api.di.Inject} and contains + * the fully qualified names of classes with the {@link Named} annotation. + * + * @throws IOException if there is an error reading or writing the file + */ + private void updateFileIfChanged() throws IOException { + String path = "META-INF/maven/org.apache.maven.api.di.Inject"; + Set existingClasses = new TreeSet<>(); // Using TreeSet for natural ordering + String existingContent = ""; + + // Try to read existing content + try { + FileObject inputFile = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", path); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputFile.openInputStream()))) { + String line; + StringBuilder contentBuilder = new StringBuilder(); + while ((line = reader.readLine()) != null) { + if (!line.trim().startsWith("#")) { + existingClasses.add(line.trim()); + } + contentBuilder.append(line).append("\n"); + } + existingContent = contentBuilder.toString(); + } + } catch (IOException e) { + logMessage(Diagnostic.Kind.NOTE, "Unable to read existing file. Proceeding with empty content."); + } + + Set allClasses = new TreeSet<>(existingClasses); // Using TreeSet for natural ordering + allClasses.addAll(processedClasses); + + StringBuilder newContentBuilder = new StringBuilder(); + for (String className : allClasses) { + newContentBuilder.append(className).append("\n"); + } + String newContent = newContentBuilder.toString(); + + if (!newContent.equals(existingContent)) { + logMessage(Diagnostic.Kind.NOTE, "Content has changed. Updating file."); + try { + FileObject outputFile = + processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", path); + try (Writer writer = outputFile.openWriter()) { + writer.write(newContent); + } + } catch (IOException e) { + logError("Failed to write to file", e); + throw e; // Re-throw to ensure the compilation fails + } + } else { + logMessage(Diagnostic.Kind.NOTE, "Content unchanged. Skipping file update."); + } + } + + /** + * Logs a message to the annotation processing environment. + * + * @param kind the kind of diagnostic message + * @param message the message to log + */ + private void logMessage(Diagnostic.Kind kind, String message) { + processingEnv.getMessager().printMessage(kind, message); + } + + /** + * Logs an error message with exception details to the annotation processing environment. + * + * @param message the error message + * @param e the exception that occurred + */ + private void logError(String message, Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + String stackTrace = sw.toString(); + + String fullMessage = message + "\n" + "Exception: " + + e.getClass().getName() + "\n" + "Message: " + + e.getMessage() + "\n" + "Stack trace:\n" + + stackTrace; + + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, fullMessage); + } +} diff --git a/api/maven-api-di/src/main/java/org/apache/maven/di/tool/package-info.java b/api/maven-api-di/src/main/java/org/apache/maven/di/tool/package-info.java new file mode 100644 index 000000000000..e7409a078d08 --- /dev/null +++ b/api/maven-api-di/src/main/java/org/apache/maven/di/tool/package-info.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides tools for processing Maven dependency injection annotations at compile time. + *

+ * This package contains annotation processors that generate metadata files used by + * the Maven dependency injection system. The main component is the {@link org.apache.maven.di.tool.DiIndexProcessor}, + * which processes classes annotated with {@link org.apache.maven.api.di.Named} and creates an index file + * that allows for efficient discovery of injectable components at runtime. + *

+ * The generated index is stored at {@code META-INF/maven/org.apache.maven.api.di.Inject} and contains + * the fully qualified names of all classes annotated with {@code @Named}. + * + * @since 4.0.0 + */ +package org.apache.maven.di.tool; diff --git a/api/maven-api-di/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/api/maven-api-di/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000000..65c417767f32 --- /dev/null +++ b/api/maven-api-di/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +org.apache.maven.di.tool.DiIndexProcessor \ No newline at end of file diff --git a/api/maven-api-di/src/site/site.xml b/api/maven-api-di/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-di/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + +

+ + + + + + + + + + diff --git a/api/maven-api-metadata/pom.xml b/api/maven-api-metadata/pom.xml new file mode 100644 index 000000000000..311aad02c46d --- /dev/null +++ b/api/maven-api-metadata/pom.xml @@ -0,0 +1,83 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-metadata + + Maven 4 API :: Repository Metadata + Maven 4 API - Immutable Repository Metadata model. + + + + org.apache.maven + maven-api-annotations + + + + + + + org.codehaus.modello + modello-maven-plugin + + ${project.basedir}/../../src/mdo + 1.2.0 + + src/main/mdo/metadata.mdo + + + + + + packageModelV4=org.apache.maven.api.metadata + + + + + modello + + velocity + xdoc + xsd + + generate-sources + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/package-info.java + + + + + + + diff --git a/api/maven-api-metadata/src/main/java/org/apache/maven/api/metadata/package-info.java b/api/maven-api-metadata/src/main/java/org/apache/maven/api/metadata/package-info.java new file mode 100644 index 000000000000..b53290b18c2a --- /dev/null +++ b/api/maven-api-metadata/src/main/java/org/apache/maven/api/metadata/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Maven Repository Metadata model. + */ +package org.apache.maven.api.metadata; diff --git a/api/maven-api-metadata/src/main/mdo/metadata.mdo b/api/maven-api-metadata/src/main/mdo/metadata.mdo new file mode 100644 index 000000000000..fe9436046eed --- /dev/null +++ b/api/maven-api-metadata/src/main/mdo/metadata.mdo @@ -0,0 +1,410 @@ + + + + repository-metadata + Metadata + Per-directory repository metadata repository-metadata.xml.

+

A directory may represent 3 types of content: "groupId", "groupId/artifactId" or "groupId/artifactId/version".

+

Most metadata content has a meaning when the directory represents a "groupId/artifactId" (groupId, artifactId, versioning)

]]> + + + + package + org.apache.maven.artifact.repository.metadata + + + + + Metadata + 1.0.0+ + + + modelVersion + 1.1.0+ + String + The version of the underlying metadata model. + + + groupId + 1.0.0+ + String + The groupId when this directory represents "groupId/artifactId" or "groupId/artifactId/version". + + + artifactId + 1.0.0+ + String + The artifactId when this directory represents "groupId/artifactId" or "groupId/artifactId/version". + + + versioning + 1.0.0+ + + Versioning + + Versioning information when this directory represents "groupId/artifactId" or "groupId/artifactId/version". + + + version + 1.0.0+ + String + The base version (i.e. ending in {@code -SNAPSHOT}) when this directory represents a "groupId/artifactId/version" for a SNAPSHOT. + + + plugins + 1.0.0+ + The set of plugins when this directory represents a "groupId". + + Plugin + * + + + + + + 1.0.0/1.1.0 + = 0 ) + { + changed = true; + v.setLastUpdated( versioning.getLastUpdated() ); + + if ( versioning.getRelease() != null ) + { + changed = true; + v.setRelease( versioning.getRelease() ); + } + if ( versioning.getLatest() != null ) + { + changed = true; + v.setLatest( versioning.getLatest() ); + } + + Snapshot s = v.getSnapshot(); + Snapshot snapshot = versioning.getSnapshot(); + if ( snapshot != null ) + { + boolean updateSnapshotVersions = false; + if ( s == null ) + { + s = new Snapshot(); + v.setSnapshot( s ); + changed = true; + updateSnapshotVersions = true; + } + + // overwrite + if ( s.getTimestamp() == null ? snapshot.getTimestamp() != null + : !s.getTimestamp().equals( snapshot.getTimestamp() ) ) + { + s.setTimestamp( snapshot.getTimestamp() ); + changed = true; + updateSnapshotVersions = true; + } + if ( s.getBuildNumber() != snapshot.getBuildNumber() ) + { + s.setBuildNumber( snapshot.getBuildNumber() ); + changed = true; + } + if ( s.isLocalCopy() != snapshot.isLocalCopy() ) + { + s.setLocalCopy( snapshot.isLocalCopy() ); + changed = true; + } + if ( updateSnapshotVersions ) + { + java.util.Map versions = new java.util.LinkedHashMap<>(); + // never convert from legacy to new format if either source or target is legacy format + if ( !v.getSnapshotVersions().isEmpty() ) + { + for ( SnapshotVersion sv : versioning.getSnapshotVersions() ) + { + String key = getSnapshotVersionKey( sv ); + versions.put( key, sv ); + } + // never convert from legacy format + if ( !versions.isEmpty() ) + { + for ( SnapshotVersion sv : v.getSnapshotVersions() ) + { + String key = getSnapshotVersionKey( sv ); + if ( !versions.containsKey( key ) ) + { + versions.put( key, sv ); + } + } + } + v.setSnapshotVersions( new java.util.ArrayList( versions.values() ) ); + } + + changed = true; + } + } + } + } + return changed; + } + ]]> + + + + + Versioning + 1.0.0+ + Versioning information for "groupId/artifactId" or "groupId/artifactId/version" SNAPSHOT + + + latest + 1.0.0+ + String + What the last version added to the directory is, including both releases and snapshots ("groupId/artifactId" directory only) + + + release + 1.0.0+ + String + What the last version added to the directory is, for the releases only ("groupId/artifactId" directory only) + + + versions + 1.0.0+ + Versions available of the artifact (both releases and snapshots) ("groupId/artifactId" directory only) + + String + * + + + + lastUpdated + 1.0.0+ + String + When the metadata was last updated (both "groupId/artifactId" and "groupId/artifactId/version" directories). The timestamp is expressed using UTC in the format yyyyMMddHHmmss. + + + snapshot + 1.0.0+ + + Snapshot + + The current snapshot data in use for this version ("groupId/artifactId/version" only) + + + snapshotVersions + 1.1.0+ + Information for each sub-artifact available in this artifact snapshot. This is only the most recent SNAPSHOT for each unique extension/classifier combination. + + SnapshotVersion + * + + + + + + 1.0.0/1.1.0 + + + + + + Snapshot + 1.0.0+ + Snapshot data for the last artifact corresponding to the SNAPSHOT base version + + + timestamp + 1.0.0+ + The timestamp when this version was deployed. The timestamp is expressed using UTC in the format yyyyMMdd.HHmmss. + String + + + buildNumber + 1.0.0+ + The incremental build number + int + + + localCopy + 1.0.0+ + Whether to use a local copy instead (with filename that includes the base version) + boolean + false + + + + + SnapshotVersion + 1.1.0+ + Versioning information for a sub-artifact of the current snapshot artifact. + + + classifier + 1.1.0+ + String + The classifier of the sub-artifact. Each classifier and extension pair may only appear once. + + true + + + extension + 1.1.0+ + String + The file extension of the sub-artifact. Each classifier and extension pair may only appear once. + true + + + version + 1.1.0+ + String + The resolved snapshot version of the sub-artifact. + true + + + updated + 1.1.0+ + String + The timestamp when this version information was last updated. The timestamp is expressed using UTC in the format yyyyMMddHHmmss. + true + + + + + + Plugin + 1.0.0+ + Mapping information for a single plugin within this group. + NOTE: plugin version is _NOT_ included here, since it is resolved using a separate algorithm in plugins' artifact. + + + name + String + true + 1.0.0+ + Display name for the plugin. + + + prefix + String + true + 1.0.0+ + The plugin invocation prefix (i.e. eclipse for eclipse:eclipse) + + + artifactId + String + true + 1.0.0+ + The plugin artifactId + + + + + diff --git a/api/maven-api-metadata/src/site/apt/index.apt b/api/maven-api-metadata/src/site/apt/index.apt new file mode 100644 index 000000000000..a3f76ef0a6e5 --- /dev/null +++ b/api/maven-api-metadata/src/site/apt/index.apt @@ -0,0 +1,33 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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 +~~ +~~ http://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. + + ----- + Introduction + ----- + Guillaume Nodet + ----- + 2024-04-04 + ----- + +Maven 4 API - Repository Metadata Model + + This is the immutable model for Repository Metadata in <<>> package. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with <<>> inner classes for immutable instances creation. + diff --git a/api/maven-api-metadata/src/site/site.xml b/api/maven-api-metadata/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-metadata/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + +

+ + + + + + + + + + diff --git a/api/maven-api-model/pom.xml b/api/maven-api-model/pom.xml new file mode 100644 index 000000000000..25a7648278db --- /dev/null +++ b/api/maven-api-model/pom.xml @@ -0,0 +1,98 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-model + + Maven 4 API :: Model + Maven 4 API - Immutable Model for Maven POM (Project Object Model). + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-xml + + + + + + + org.codehaus.modello + modello-maven-plugin + + 4.2.0 + ${project.basedir}/../../src/mdo + + src/main/mdo/maven.mdo + + + + + + packageModelV4=org.apache.maven.api.model + isMavenModel=true + + + + + modello + + velocity + xdoc + xsd + + generate-sources + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + attach-artifact + + + + + ${basedir}/src/main/mdo/maven.mdo + mdo + + + + + + + + + + diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocation.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocation.java new file mode 100644 index 000000000000..2e65dea793fd --- /dev/null +++ b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocation.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.model; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Represents the location of an element within a model source file. + *

+ * This class tracks the line and column numbers of elements in source files like POM files. + * It's used for error reporting and debugging to help identify where specific model elements + * are defined in the source files. + * + * @since 4.0.0 + */ +public class InputLocation implements Serializable, InputLocationTracker { + private final int lineNumber; + private final int columnNumber; + private final InputSource source; + private final Map locations; + private final InputLocation importedFrom; + + public InputLocation(InputSource source) { + this.lineNumber = -1; + this.columnNumber = -1; + this.source = source; + this.locations = Collections.singletonMap(0, this); + this.importedFrom = null; + } + + public InputLocation(int lineNumber, int columnNumber) { + this(lineNumber, columnNumber, null, null); + } + + public InputLocation(int lineNumber, int columnNumber, InputSource source) { + this(lineNumber, columnNumber, source, null); + } + + public InputLocation(int lineNumber, int columnNumber, InputSource source, Object selfLocationKey) { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.source = source; + this.locations = + selfLocationKey != null ? Collections.singletonMap(selfLocationKey, this) : Collections.emptyMap(); + this.importedFrom = null; + } + + public InputLocation(int lineNumber, int columnNumber, InputSource source, Map locations) { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.source = source; + this.locations = ImmutableCollections.copy(locations); + this.importedFrom = null; + } + + public InputLocation(InputLocation original) { + this.lineNumber = original.lineNumber; + this.columnNumber = original.columnNumber; + this.source = original.source; + this.locations = original.locations; + this.importedFrom = original.importedFrom; + } + + public int getLineNumber() { + return lineNumber; + } + + public int getColumnNumber() { + return columnNumber; + } + + public InputSource getSource() { + return source; + } + + @Override + public InputLocation getLocation(Object key) { + return locations != null ? locations.get(key) : null; + } + + public Map getLocations() { + return locations; + } + + /** + * Gets the parent InputLocation where this InputLocation may have been imported from. + * Can return {@code null}. + * + * @return InputLocation + * @since 4.0.0 + */ + @Override + public InputLocation getImportedFrom() { + return importedFrom; + } + + /** + * Merges the {@code source} location into the {@code target} location. + * + * @param target the target location + * @param source the source location + * @param sourceDominant the boolean indicating of {@code source} is dominant compared to {@code target} + * @return the merged location + */ + public static InputLocation merge(InputLocation target, InputLocation source, boolean sourceDominant) { + if (source == null) { + return target; + } else if (target == null) { + return source; + } + + Map locations; + Map sourceLocations = source.locations; + Map targetLocations = target.locations; + if (sourceLocations == null) { + locations = targetLocations; + } else if (targetLocations == null) { + locations = sourceLocations; + } else { + locations = new LinkedHashMap<>(); + locations.putAll(sourceDominant ? targetLocations : sourceLocations); + locations.putAll(sourceDominant ? sourceLocations : targetLocations); + } + + return new InputLocation(-1, -1, InputSource.merge(source.getSource(), target.getSource()), locations); + } // -- InputLocation merge( InputLocation, InputLocation, boolean ) + + /** + * Merges the {@code source} location into the {@code target} location. + * This method is used when the locations refer to lists and also merges the indices. + * + * @param target the target location + * @param source the source location + * @param indices the list of integers for the indices + * @return the merged location + */ + public static InputLocation merge(InputLocation target, InputLocation source, Collection indices) { + if (source == null) { + return target; + } else if (target == null) { + return source; + } + + Map locations; + Map sourceLocations = source.locations; + Map targetLocations = target.locations; + if (sourceLocations == null) { + locations = targetLocations; + } else if (targetLocations == null) { + locations = sourceLocations; + } else { + locations = new LinkedHashMap<>(); + for (int index : indices) { + InputLocation location; + if (index < 0) { + location = sourceLocations.get(~index); + } else { + location = targetLocations.get(index); + } + locations.put(locations.size(), location); + } + } + + return new InputLocation(-1, -1, InputSource.merge(source.getSource(), target.getSource()), locations); + } // -- InputLocation merge( InputLocation, InputLocation, java.util.Collection ) + + /** + * Class StringFormatter. + * + * @version $Revision$ $Date$ + */ + public interface StringFormatter { + + // -----------/ + // - Methods -/ + // -----------/ + + /** + * Method toString. + */ + String toString(InputLocation location); + } + + @Override + public String toString() { + return String.format("%s @ %d:%d", source != null ? source.getLocation() : "n/a", lineNumber, columnNumber); + } +} diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java new file mode 100644 index 000000000000..8b2958a35cc6 --- /dev/null +++ b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.model; + +public interface InputLocationTracker { + InputLocation getLocation(Object field); + + /** + * Gets the parent InputLocation where this InputLocation may have been imported from. + * Can return {@code null}. + * + * @return InputLocation + * @since 4.0.0 + */ + InputLocation getImportedFrom(); +} diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputSource.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputSource.java new file mode 100644 index 000000000000..f4d5e7fc67bf --- /dev/null +++ b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputSource.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.model; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Represents the source of a model input, such as a POM file. + *

+ * This class tracks the origin of model elements, including their location in source files + * and relationships between imported models. It's used for error reporting and debugging + * to help identify where specific model elements came from. + * + * @since 4.0.0 + */ +public class InputSource implements Serializable { + + private final String modelId; + private final String location; + private final List inputs; + private final InputLocation importedFrom; + + public InputSource(String modelId, String location) { + this(modelId, location, null); + } + + public InputSource(String modelId, String location, InputLocation importedFrom) { + this.modelId = modelId; + this.location = location; + this.inputs = null; + this.importedFrom = importedFrom; + } + + public InputSource(Collection inputs) { + this.modelId = null; + this.location = null; + this.inputs = ImmutableCollections.copy(inputs); + this.importedFrom = null; + } + + /** + * Get the path/URL of the POM or {@code null} if unknown. + * + * @return the location + */ + public String getLocation() { + return this.location; + } + + /** + * Get the identifier of the POM in the format {@code ::}. + * + * @return the model id + */ + public String getModelId() { + return this.modelId; + } + + /** + * Gets the parent InputLocation where this InputLocation may have been imported from. + * Can return {@code null}. + * + * @return InputLocation + * @since 4.0.0 + */ + public InputLocation getImportedFrom() { + return importedFrom; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InputSource that = (InputSource) o; + return Objects.equals(modelId, that.modelId) + && Objects.equals(location, that.location) + && Objects.equals(inputs, that.inputs); + } + + @Override + public int hashCode() { + return Objects.hash(modelId, location, inputs); + } + + Stream sources() { + return inputs != null ? inputs.stream() : Stream.of(this); + } + + @Override + public String toString() { + if (inputs != null) { + return inputs.stream().map(InputSource::toString).collect(Collectors.joining(", ", "merged[", "]")); + } + return getModelId() + " " + getLocation(); + } + + public static InputSource merge(InputSource src1, InputSource src2) { + return new InputSource(Stream.concat(src1.sources(), src2.sources()).collect(Collectors.toSet())); + } +} diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java new file mode 100644 index 000000000000..450e326cbc59 --- /dev/null +++ b/api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Maven Immutable POM (Project Object Model) classes, generated from maven.mdo model. + *

+ * This package contains the data model classes that represent the structure of Maven POM files. + * These classes are immutable to ensure thread safety and prevent unintended modifications. + * The root class is {@link org.apache.maven.api.model.Model}, which represents the entire POM. + *

+ * Key components include: + *

    + *
  • {@link org.apache.maven.api.model.Model} - The root element of a POM file
  • + *
  • {@link org.apache.maven.api.model.Dependency} - Represents a project dependency
  • + *
  • {@link org.apache.maven.api.model.Plugin} - Represents a Maven plugin configuration
  • + *
  • {@link org.apache.maven.api.model.Build} - Contains build configuration information
  • + *
  • {@link org.apache.maven.api.model.Profile} - Represents a build profile for conditional execution
  • + *
+ * + * @since 4.0.0 + */ +package org.apache.maven.api.model; diff --git a/api/maven-api-model/src/main/mdo/maven.mdo b/api/maven-api-model/src/main/mdo/maven.mdo new file mode 100644 index 000000000000..8863ef01641f --- /dev/null +++ b/api/maven-api-model/src/main/mdo/maven.mdo @@ -0,0 +1,3613 @@ + + + + + + + maven + Maven + + This is a reference for the Maven project descriptor used in Maven.

+

An XSD is available at:

+ + ]]> +
+ + + package + org.apache.maven.model + + + + + Model + ModelBase + + The {@code <project>} element is the root of the descriptor. + The following table lists all of the possible child elements. + + 3.0.0+ + + + + pomFile + 4.1.0+ + false + Originating POM file + DOM + + + + + + + + modelVersion + 4.0.0+ + true + Declares to which version of project descriptor this POM conforms. + String + + + + + + + + parent + 4.0.0+ + The location of the parent project, if one exists. Values from the parent + project will be the default for this project if they are left unspecified. The location + is given as a group ID, artifact ID and version. + + Parent + + + + + + + + + mixins + 4.2.0+ + Mixins... + + Mixin + * + + + + + + + + + groupId + 3.0.0+ + true + + A universally unique identifier for a project. It is normal to + use a fully-qualified package name to distinguish it from other + projects with a similar name (eg. {@code org.apache.maven}). + + String + + + artifactId + 3.0.0+ + true + The identifier for this artifact that is unique within the group given by the + group ID. An artifact is something that is either produced or used by a project. + Examples of artifacts produced by Maven for a project include: JARs, source and binary + distributions, and WARs. + String + + + version + 4.0.0+ + true + The current version of the artifact produced by this project. + String + + + packaging + 4.0.0+ + + The type of artifact this project produces, for example {@code jar}, + {@code war}, + {@code ear}, + {@code pom}. + Plugins can create their own packaging, and + therefore their own packaging types, + so this list does not contain all possible types. + + String + jar + + + + + + + + name + 3.0.0+ + true + The full name of the project. + String + + + description + 3.0.0+ + A detailed description of the project, used by Maven whenever it needs to + describe the project, such as on the website. While this element can be specified as + CDATA to enable the use of HTML tags within the description, it is discouraged to allow + plain text representation. If you need to modify the index page of the generated website, + you are able to specify your own instead of adjusting this text. + String + + + url + 3.0.0+ + + Default value is: parent value [+ path adjustment] + (artifactId or project.directory property), or just parent value if + project's {@code child.project.url.inherit.append.path="false"}

+ ]]> +
+ String +
+ + childProjectUrlInheritAppendPath + 4.0.0+ + + Default value is: {@code true}

+ @since Maven 3.6.1 + ]]> +
+ String +
+ + root + 4.1.0+ + + Indicates that this project is the root project, located in the upper directory of the source tree. + This is the directory which may contain the .mvn directory. + + @since Maven 4.0.0 + + boolean + false + + + preserveModelVersion + 4.1.0+ + + Indicates if the build POM for this project should be preserved or downgraded to the lowest + compatible version. + @since Maven 4.0.0 + + boolean + false + + + inceptionYear + 3.0.0+ + true + The year of the project's inception, specified with 4 digits. This value is + used when generating copyright notices as well as being informational. + String + + + organization + 3.0.0+ + This element describes various attributes of the organization to which the + project belongs. These attributes are utilized when documentation is created (for + copyright notices and links). + organisation + + Organization + + + + licenses + 3.0.0+ + + + + + License + * + + + + developers + 3.0.0+ + Describes the committers of a project. + + Developer + * + + + + contributors + 3.0.0+ + Describes the contributors to a project that are not yet committers. + + Contributor + * + + + + mailingLists + 3.0.0+ + Contains information about a project's mailing lists. + + MailingList + * + + + + + + + + + prerequisites + 4.0.0+ + Describes the prerequisites in the build environment for this project. + + Prerequisites + + + + + + + + + scm + 4.0.0+ + Specification for the SCM used by the project, such as CVS, Subversion, etc. + + Scm + + + + + + + + + issueManagement + 4.0.0+ + The project's issue management system information. + + IssueManagement + + + + + + + + + ciManagement + 4.0.0+ + The project's continuous integration information. + + CiManagement + + + + + + + + + build + 3.0.0+ + true + Information required to build the project. + + Build + + + + + + + + + profiles + 4.0.0+ + A listing of project-local build profiles which will modify the build process + when activated. + + Profile + * + + +
+ + + 4.0.0/4.0.99 + + + + + + 4.1.0+ + + + + + +
+ + ModelBase + 3.0.0+ + + Base class for the {@code Model} and the {@code Profile} objects. + + + + modules + 4.0.0/4.2.0 + + @deprecated Use {@link #subprojects} instead. + + + String + * + + + @Deprecated(since = "4.0.0") + + + + subprojects + 4.1.0+ + The subprojects (formerly called modules) to build as a part of this + project. Each subproject listed is a relative path to the directory containing the subproject. + To be consistent with the way default URLs are calculated from parent, it is recommended + to have subproject names match artifact ids. + + String + * + + + + distributionManagement + 4.0.0+ + Distribution information for a project that enables deployment of the site + and artifacts to remote web servers and repositories respectively. + + DistributionManagement + + + + properties + 4.0.0+ + + Properties that can be used throughout the POM as a substitution, and + are used as filters in resources if enabled. + The format is {@code <name>value</name>}. + + Properties + + String + * + + + + dependencyManagement + 4.0.0+ + false + Default dependency information for projects that inherit from this one. The + dependencies in this section are not immediately resolved. Instead, when a POM derived + from this one declares a dependency described by a matching groupId and artifactId, the + version and other values from this section are used for that dependency if they were not + already specified. + + DependencyManagement + + + + dependencies + 3.0.0+ + + This element describes all the dependencies associated with a project. + These dependencies are used to construct a classpath for your + project during the build process. They are automatically downloaded from the + repositories defined in this project. + + @see <a href="https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html">Dependency mechanism</a> + + + Dependency + * + + + + repositories + 4.0.0+ + The lists of the remote repositories for discovering dependencies and + extensions. + + Repository + * + + + + pluginRepositories + 4.0.0+ + The lists of the remote repositories for discovering plugins for builds and + reports. + + Repository + * + + + + reports + 4.0.0 + + @deprecated Now ignored by Maven. + + DOM + + @Deprecated(since = "4.0.0") + + + + reporting + 4.0.0+ + + This element includes the specification of report plugins to use + to generate the reports on the Maven-generated site. + These reports will be run when a user executes {@code mvn site}. + All the reports will be included in the navigation bar for browsing. + + + Reporting + + + + + + PluginContainer + 3.0.0+ + Contains the plugins information for the project. + + + plugins + 4.0.0+ + The list of plugins to use. + + Plugin + * + + + + + + 4.0.0/4.0.99 + + getPluginsAsMap() { + return getPlugins().stream().collect(Collectors.toMap(plugin -> plugin.getKey(), plugin -> plugin)); + } + ]]> + + + + 4.1.0+ + + pluginMap; + + /** + * @return a Map of plugins field with {@code Plugins#getKey()} as key + * @see Plugin#getKey() + */ + public Map getPluginsAsMap() { + if (pluginMap == null) { + synchronized (this) { + if (pluginMap == null) { + pluginMap = ImmutableCollections.copy(plugins.stream().collect( + java.util.stream.Collectors.toMap( + Plugin::getKey, java.util.function.Function.identity()))); + } + } + } + return pluginMap; + } + ]]> + + + + 4.0.0+ + + + + + + + + PluginConfiguration + 3.0.0+ + PluginContainer + Contains the plugins management information for the project. + + + + pluginManagement + 4.0.0+ + false + Default plugin information to be made available for reference by projects + derived from this one. This plugin configuration will not be resolved or bound to the + lifecycle unless referenced. Any local configuration for a given plugin will override + the plugin's entire definition here. + + PluginManagement + + + + + + 4.0.0+ + + + + + + + + BuildBase + 3.0.0+ + PluginConfiguration + Build configuration in a profile. + + + defaultGoal + 3.0.0+ + The default goal to execute when none is specified for + the project. Note that in case of a build with subprojects, only the default goal of the top-level + project is relevant, i.e. the default goals of subprojects are ignored. Since Maven 3, + multiple goals/phases can be separated by whitespace. + String + + + resources + 3.0.0+ + + This element describes all the classpath resources such as properties + files associated with a project. These resources are often included in the final + package. + The default value is {@code src/main/resources}. + + @deprecated Replaced by {@code <Source>} with {@code main} scope and {@code resources} language. + + + Resource + * + + + @Deprecated(since = "4.0.0") + + + + testResources + 4.0.0+ + + This element describes all the classpath resources such as properties + files associated with a project's unit tests. + The default value is {@code src/test/resources}. + + @deprecated Replaced by {@code <Source>} with {@code test} scope and {@code resources} language. + + + Resource + * + + + @Deprecated(since = "4.0.0") + + + + directory + 4.0.0+ + + The directory where all files generated by the build are placed. + The default value is {@code target}. + + String + + + finalName + 4.0.0+ + + The filename (excluding the extension, and with no path information) that + the produced artifact will be called. + The default value is {@code ${artifactId}-${version}}. + + String + + + filters + 4.0.0+ + The list of filter properties files that are used when filtering is enabled. + + String + * + + + + + + 4.0.0+ + + + + + + + + Build + 3.0.0+ + BuildBase + + The {@code <build>} element contains information required to build the project. + Default values are defined in Super POM. + + + + sources + 4.1.0+ + + All the sources to compile and resources files to copy for a project or it's unit tests. + The sources can be Java source files, generated source files, scripts, or resources for examples. + Each source is specified by a {@code directory} element, which is relative to the POM. + The kind of sources (source files to compile or resources to copy) and their usage (for the main code + or for the tests) is specified by the {@code scope} element together with each source directory. + + + Source + * + + + + sourceDirectory + 3.0.0+ + true + + This element specifies a directory containing the source of the project. The + generated build system will compile the sources from this directory when the project is + built. The path given is relative to the project descriptor. + The default value is {@code src/main/java}. + + @deprecated Replaced by {@code <Source>} with {@code main} scope. + + String + + @Deprecated(since = "4.0.0") + + + + scriptSourceDirectory + 4.0.0+ + true + + This element specifies a directory containing the script sources of the + project. This directory is meant to be different from the sourceDirectory, in that its + contents will be copied to the output directory in most cases (since scripts are + interpreted rather than compiled). + The default value is {@code src/main/scripts}. + + @deprecated Replaced by {@code <Source>} with {@code script} language. + + String + + @Deprecated(since = "4.0.0") + + + + testSourceDirectory + 4.0.0+ + true + + This element specifies a directory containing the unit test source of the + project. The generated build system will compile these directories when the project is + being tested. The path given is relative to the project descriptor. + The default value is {@code src/test/java}. + + @deprecated Replaced by {@code <Source>} with {@code test} scope. + + String + + @Deprecated(since = "4.0.0") + + + + outputDirectory + 4.0.0+ + + The directory where compiled application classes are placed. + The default value is {@code target/classes}. + + String + + + testOutputDirectory + 4.0.0+ + + The directory where compiled test classes are placed. + The default value is {@code target/test-classes}. + + String + + + extensions + 4.0.0+ + A set of build extensions to use from this project. + + Extension + * + + + + + + 4.0.0+ + + + + + + + + CiManagement + 4.0.0+ + + } element contains information required to the + continuous integration system of the project. + ]]> + + + + system + 4.0.0+ + + The name of the continuous integration system, e.g. {@code continuum}. + + String + + + url + 4.0.0+ + + URL for the continuous integration system used by the project if it has a web interface. + + String + + + notifiers + 4.0.0+ + Configuration for notifying developers/users when a build is unsuccessful, + including user information and notification mode. + + * + Notifier + + + + + + Notifier + Configures one method for notifying users/developers when a build breaks. + 4.0.0+ + + + type + 4.0.0+ + mail + String + The mechanism used to deliver notifications. + + + sendOnError + 4.0.0+ + true + boolean + Whether to send notifications on error. + + + sendOnFailure + 4.0.0+ + true + boolean + Whether to send notifications on failure. + + + sendOnSuccess + 4.0.0+ + true + boolean + Whether to send notifications on success. + + + sendOnWarning + 4.0.0+ + true + boolean + Whether to send notifications on warning. + + + + address + 4.0.0+ + String + + @deprecated Where to send the notification to - eg email address. + + + @Deprecated(since = "4.0.0") + + + + configuration + Extended configuration specific to this notifier goes here. + Properties + + String + * + + + + + + Contributor + Description of a person who has contributed to the project, but who does not have + commit privileges. Usually, these contributions come in the form of patches submitted. + 3.0.0+ + + + name + 3.0.0+ + The full name of the contributor. + String + + + email + 3.0.0+ + The email address of the contributor. + String + + + url + 3.0.0+ + The URL for the homepage of the contributor. + String + + + + organization + organisation + 3.0.0+ + The organization to which the contributor belongs. + String + + + organizationUrl + organisationUrl + 3.0.0+ + The URL of the organization. + String + + + roles + 3.0.0+ + + The roles the contributor plays in the project. Each role is described by a + {@code role} element, the body of which is a role name. This can also be used to + describe the contribution. + + + String + * + + + + timezone + 3.0.0+ + + -12 to +14 + or a valid time zone id like "America/Montreal" (UTC-05:00) or "Europe/Paris" (UTC+01:00). + ]]> + + String + + + properties + 3.0.0+ + Properties about the contributor, such as an instant messenger handle. + Properties + + String + * + + + + + + 4.0.0+ + + + + + + + + Dependency + 3.0.0+ + + } element contains information about a dependency + of the project. + ]]> + + + + groupId + 3.0.0+ + true + + + + String + + + artifactId + 3.0.0+ + true + + The unique id for an artifact produced by the project group, e.g. + {@code maven-artifact}. + + String + + + version + 3.0.0+ + + predictability of resolved version. + See dependency version requirement documentation + and transitive dependencies resolution for more details. + ]]> + + String + + + type + 4.0.0+ + + default + artifact handlers for a list. New types can be defined by extensions, so this is not a complete list. + ]]> + + String + jar + + + classifier + 4.0.0+ + + +
  • referring to attached artifact, for example {@code sources} and {@code javadoc}: + see default artifact handlers for a list,
  • +
  • distinguishing two artifacts + that belong to the same POM but were built differently. + For example, {@code jdk14} and {@code jdk15}.
  • + + ]]> +
    + String + false +
    + + scope + 4.0.0+ + + the + dependency mechanism. The default scope is {@code compile}. + ]]> + + String + + + + + systemPath + 4.0.0+ + + discouraged + and may be replaced in later versions. This specifies the path on the filesystem + for this dependency. + Requires an absolute path for the value, not relative. + Use a property that gives the machine specific absolute path, + e.g. {@code ${java.home}}. + ]]> + + String + + + exclusions + 4.0.0+ + Lists a set of artifacts that should be excluded from this dependency's + artifact list when it comes to calculating transitive dependencies. + + Exclusion + * + + + + optional + 4.0.0+ + + Indicates the dependency is optional for use of this library. While the + version of the dependency will be taken into account for dependency calculation if the + library is used elsewhere, it will not be passed on transitively. Note: While the type + of this field is {@code String} for technical reasons, the semantic type is actually + {@code Boolean}. Default value is {@code false}. + + String + +
    + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + + + + + 4.0.0+ + + + + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + public void clearManagementKey() { + managementKey = null; + } + + + + +
    + + Contributor + Developer + Information about one of the committers on this project. + 3.0.0+ + + + id + 3.0.0+ + The unique ID of the developer in the SCM. + String + + + + + 4.0.0+ + + + + + + + + Exclusion + 4.0.0+ + + } element contains information required to exclude + an artifact to the project. +

    + The {@code groupId} and {@code artifactId} fields are interpreted as glob patterns. + + @see java.nio.file.FileSystem#getPathMatcher + ]]> + + + + groupId + 4.0.0+ + The group ID of the project to exclude. + String + true + + + artifactId + 4.0.0+ + The artifact ID of the project to exclude. + String + true + + + + + IssueManagement + Information about the issue tracking (or bug tracking) system used to manage this + project. + 4.0.0+ + + + system + 4.0.0+ + The name of the issue management system, e.g. Bugzilla + String + + + url + 4.0.0+ + URL for the issue management system used by the project. + String + + + + + 4.0.0+ + + + + + + + + DistributionManagement + 4.0.0+ + This elements describes all that pertains to distribution for a project. It is + primarily used for deployment of artifacts and the site produced by the build. + + + repository + 4.0.0+ + Information needed to deploy the artifacts generated by the project to a + remote repository. + + DeploymentRepository + + + + snapshotRepository + 4.0.0+ + + Where to deploy snapshots of artifacts to. If not given, it defaults to the + {@code repository} element. + + + DeploymentRepository + + + + site + Information needed for deploying the web site of the project. + 4.0.0+ + + Site + + + + downloadUrl + 4.0.0+ + + The URL of the project's download page. If not given users will be + referred to the homepage given by {@code url}. + This is given to assist in locating artifacts that are not in the repository due to + licensing restrictions. + + String + + + relocation + 4.0.0+ + + Relocation information of the artifact if it has been moved to a new group ID + and/or artifact ID. + + + Relocation + + + + status + 4.0.0+ + + Gives the status of this artifact in the remote repository. + This must not be set in your local project, as it is updated by + tools placing it in the repository. Valid values are: {@code none} (default), + {@code converted} (repository manager converted this from a Maven 1 POM), + {@code partner} + (directly synced from a partner Maven 2 repository), {@code deployed} (was deployed from a Maven 2 + instance), {@code verified} (has been hand verified as correct and final). + + false + String + + + + + License + Describes the licenses for this project. This is used to generate the license + page of the project's website, as well as being taken into consideration in other reporting + and validation. The licenses listed for the project are that of the project itself, and not + of dependencies. + 3.0.0+ + + + name + 3.0.0+ + The full legal name of the license. + String + + + url + 3.0.0+ + The official url for the license text. + String + + + distribution + 3.0.0+ + + +

    repo
    +
    may be downloaded from the Maven repository
    +
    manual
    +
    user must manually download and install the dependency.
    + + ]]> +
    + String + + + comments + Addendum information pertaining to this license. + 3.0.0+ + String + + + + + 4.0.0+ + + + + + +
    + + MailingList + 3.0.0+ + This element describes all of the mailing lists associated with a project. The + auto-generated site references this information. + + + name + 3.0.0+ + + The name of the mailing list. + + String + + + subscribe + 3.0.0+ + + The email address or link that can be used to subscribe to + the mailing list. If this is an email address, a + {@code mailto:} link will automatically be created + when the documentation is created. + + String + + + unsubscribe + 3.0.0+ + + The email address or link that can be used to unsubscribe to + the mailing list. If this is an email address, a + {@code mailto:} link will automatically be created + when the documentation is created. + + String + + + post + 3.0.0+ + + The email address or link that can be used to post to + the mailing list. If this is an email address, a + {@code mailto:} link will automatically be created + when the documentation is created. + + String + + + archive + 3.0.0+ + The link to a URL where you can browse the mailing list archive. + String + + + otherArchives + 3.0.0+ + The link to alternate URLs where you can browse the list archive. + + String + * + + + + We could probably have a specific element for a dev mailing list for things like CI, + and maybe even a specific element for the user and scm mailing lists. Then leave the more + lose structure for any other type of mailing list. + + + 4.0.0+ + + + + + + + + Organization + Specifies the organization that produces this project. + 3.0.0+ + + + name + 3.0.0+ + The full name of the organization. + String + + + url + 3.0.0+ + The URL to the organization's home page. + String + + + + + 4.0.0+ + + + + + + + + PatternSet + 3.0.0+ + Definition of include or exclude patterns. + + + includes + 3.0.0+ + + A list of patterns to include, e.g. {@code **&#47;*.xml}. + + + String + * + + + + excludes + 3.0.0+ + + A list of patterns to exclude, e.g. {@code **&#47;*.xml} + + + String + * + + + + + + 4.0.0+ + + + + + + + + Parent + 4.0.0+ + + } element contains information required to locate the parent project from which + this project will inherit from. +

    Note: The children of this element are not interpolated and must be given as literal values.

    + ]]> +
    + + + groupId + 4.0.0+ + The group id of the parent project to inherit from. + true + String + + + artifactId + 4.0.0+ + The artifact id of the parent project to inherit from. + true + String + + + version + 4.0.0+ + The version of the parent project to inherit. + String + + + relativePath + 4.0.0+ + + The relative path of the parent subproject POM file or directory within the checkout. + If not specified, it defaults to {@code ..}, i.e. the parent directory. + Maven looks for the parent POM first in this location on + the filesystem if explicitly provided, then in the reactor if groupId and artifactId are provided, + then in the default parent directory, then the local repository, and lastly in the remote repo. + However, if the both relative path and the group ID / artifact ID are provided, + they must match the file in the location given. + Specify either the {@code relativePath} or the {@code groupId}/{@code artifactId}, not both. + + String + + + + + 4.0.0+ + + + + + + +
    + + Mixin + 4.1.0+ + Parent + + + classifier + 4.1.0+ + String + + + extension + 4.1.0+ + String + + + + + Scm + 4.0.0+ + + The {@code <scm>} element contains information required to the SCM + (Source Control Management) of the project. + + + + connection + 4.0.0+ + + Default value is: parent value [+ path adjustment] + (artifactId or project.directory property), or just parent value if + scm's {@code child.scm.connection.inherit.append.path="false"} + @see URL format + @see list of supported SCMs + ]]> + + String + + + developerConnection + 4.0.0+ + + Default value is: parent value [+ path adjustment] + (artifactId or project.directory property), or just parent value if + scm's {@code child.scm.developerConnection.inherit.append.path="false"}.

    + ]]> +
    + String +
    + + tag + 4.0.0+ + The tag of current code. By default, it's set to HEAD during development. + String + HEAD + + + url + 4.0.0+ + + Default value is: parent value [+ path adjustment] + (artifactId or project.directory property), or just parent value if + scm's {@code child.scm.url.inherit.append.path="false"}

    + ]]> +
    + String +
    + + childScmConnectionInheritAppendPath + 4.0.0+ + + Default value is: {@code true}

    + @since Maven 3.6.1 + ]]> +
    + String +
    + + childScmDeveloperConnectionInheritAppendPath + 4.0.0+ + + Default value is: {@code true}

    + @since Maven 3.6.1 + ]]> +
    + String +
    + + childScmUrlInheritAppendPath + 4.0.0+ + + Default value is: {@code true} + @since Maven 3.6.1 + ]]> + + String + +
    + + + 4.0.0+ + + + + + + 4.0.0+ + + + + + +
    + + FileSet + 3.0.0+ + PatternSet + A PatternSet for files. + + + directory + 3.0.0+ + Describe the directory where the resources are stored. The path is relative + to the POM. + String + + + + + 4.0.0+ + + + + + + + + Source + + etc.) and their + usage (main code, tests, etc.) is specified by the {@code scope} element. + +

    Default source directories

    + If no source directories are specified, the default values depend on whether module names are specified: +
      +
    • {@code src/${scope}/${lang}} if no module is specified
    • +
    • {@code src/${module}/${scope}/${lang}} if a module is specified
    • +
    + where + {@code ${scope}} is the value of the {@link #scope} element (typically {@code main} or {@code test}), + {@code ${lang}} is the value of the {@link #lang} element (typically {@code java} or {@code resources}), + and {@code ${module}} is the optional {@link #module} element. + ]]> +
    + 4.1.0+ + FileSet + + + scope + 4.1.0+ + + The main scope is used for specifying a directory containing the source of the project. + The generated build system will compile the sources from this directory when the project is built. + The path given in the {@code directory} field is relative to the project descriptor. + The default directory for the default language (Java) is {@code "src/main/java"}.

    + +

    The test scope is used for specifying a directory containing the unit test source of the project. + The generated build system will compile these directories when the project is being tested. + The path given in the {@code directory} field is relative to the project descriptor. + The default directory for the default language (Java) is {@code "src/test/java"}.

    + +

    If no scope is specified, the default is {@code main}.

    + ]]> +
    + String + main +
    + + lang + 4.1.0+ + + JSON or XML). + +

    The java language is used for specifying a directory containing the Java sources of the project. + The generated build system will compile the sources from this directory using the Java compiler when the + project is built. The path given in the {@code directory} field is relative to the project descriptor. + The default directory for the main Java sources is {@code "src/main/java"}.

    + +

    The resources language is used for specifying a directory containing the class-path + or module-path resources such as properties files or scripts associated with a project. + This directory is meant to be different from the main source directory, + in that its contents will be copied to the output directory in most cases + (since scripts are interpreted rather than compiled). + The default directory for the main resources is {@code "src/main/resources"}.

    + +

    If no language is specified, the default is {@code java}.

    + ]]> +
    + String + java +
    + + module + 4.1.0+ + + If a module name is specified for resources or script files, + then this value modifies the directory where the files will be copied. + For example, if a Java module name is "foo.biz", then the {@code foo/bar.properties} + resource file will be copied as {@code foo.biz/foo/bar.properties}.

    + +

    This element can be combined with the {@code targetVersion} element for specifying sources, + scripts, or resources that are specific to both a particular module and a target version.

    + ]]> +
    + String +
    + + targetVersion + 4.1.0+ + + JAR file will be created + with the lowest version taken as the base version. + If this element is omitted, then the default target version is the compiler default value, + which is usually the version of the Java environment running Maven. + +

    If a target version, different from the base version, is specified for resources or script files, + then this value modifies the directory where the files will be copied. + For example, if {@code targetVersion} is 17, then the {@code foo/bar.properties} + resource file will be copied as {@code META-INF/versions/17/foo/bar.properties}.

    + +

    This element can be combined with the {@code module} element for specifying sources, + scripts, or resources that are specific to both a particular module and a target version.

    + ]]> +
    + String +
    + + targetPath + 4.1.0+ + + When a target path is explicitly specified, the values of the {@code module} and {@code targetVersion} + elements are not used for inferring the path (they are still used as compiler options however). + It means that for scripts and resources, the files below the path specified by {@code directory} + are copied to the path specified by {@code targetPath} with the exact same directory structure. + It is user's responsibility to put module and version components in the {@code targetPath} if needed.

    + +

    Note that for Java source files, a directory with the module name may still be generated despite + above statement about {@code module} being ignored, because that directory is generated by the Java + compiler rather than Maven.

    + ]]> +
    + String +
    + + stringFiltering + 4.1.0+ + + The default value is {@code false}.

    + ]]> +
    + boolean + false +
    + + enabled + 4.1.0+ + + The default value is {@code true}.

    + ]]> +
    + boolean + true +
    +
    +
    + + Resource + This element describes all of the classpath resources associated with a project + or unit tests. + + @deprecated Replaced by {@code <Source>} with {@code resources} language. + + 3.0.0+ + FileSet + + @Deprecated(since = "4.0.0") + + + + targetPath + 3.0.0+ + + Describe the resource target path. The path is relative to the target/classes + directory (i.e. {@code ${project.build.outputDirectory}}). + For example, if you want that resource to appear in a specific package + ({@code org.apache.maven.messages}), you must specify this + element with this value: {@code org/apache/maven/messages}. + This is not required if you simply put the resources in that directory + structure at the source, however. + + String + + + filtering + 3.0.0+ + + Whether resources are filtered to replace tokens with parameterised values or not. + The values are taken from the {@code properties} element and from the + properties in the files listed in the {@code filters} element. Note: While the type + of this field is {@code String} for technical reasons, the semantic type is actually + {@code Boolean}. Default value is {@code false}. + + String + + + mergeId + 4.0.0 + + FOR INTERNAL USE ONLY. This is a unique identifier assigned to each + resource to allow Maven to merge changes to this resource that take + place during the execution of a plugin. This field must be managed + by the generated parser and formatter classes in order to allow it + to survive model interpolation. + + String + + + + + 4.0.0+ + + + + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + + + + + 4.0.0+ + + + + + + + + RepositoryBase + 4.0.0+ + A repository contains the information needed for establishing connections with + remote repository. + + + id + 4.0.0+ + true + true + + A unique identifier for a repository. This is used to match the repository + to configuration in the {@code settings.xml} file, for example. Furthermore, the identifier is + used during POM inheritance and profile injection to detect repositories that should be merged. + + String + + + name + 4.0.0+ + Human readable name of the repository. + String + + + url + 4.0.0+ + true + + The url of the repository, in the form {@code protocol://hostname/path}. + + String + + + layout + 4.0.0+ + + The type of layout this repository uses for locating and storing artifacts - + can be {@code legacy} or {@code default}. + + String + default + + + + + + Repository + RepositoryBase + 4.0.0+ + A repository contains the information needed for establishing connections with + remote repository. + + + releases + 4.0.0+ + How to handle downloading of releases from this repository. + + RepositoryPolicy + + + + snapshots + 4.0.0+ + How to handle downloading of snapshots from this repository. + + RepositoryPolicy + + + + + + + DeploymentRepository + Repository + 4.0.0+ + Deployment repository contains the information needed for deploying to the remote + repository, which adds uniqueVersion property to usual repositories for download. + + + uniqueVersion + Whether to assign snapshots a unique version comprised of the timestamp and + build number, or to use the same version each time + boolean + true + 4.0.0+ + + + + + + RepositoryPolicy + 4.0.0+ + Download policy. + + + enabled + 4.0.0+ + + Whether to use this repository for downloading this type of artifact. Note: While the type + of this field is {@code String} for technical reasons, the semantic type is actually + {@code Boolean}. Default value is {@code true}. + + String + + + updatePolicy + 4.0.0+ + + The frequency for downloading updates - can be + {@code always}, + {@code daily} (default), + {@code interval:XXX} (in minutes) or + {@code never} (only if it doesn't exist locally). + + String + + + checksumPolicy + 4.0.0+ + + What to do when verification of an artifact checksum fails. Valid values are + {@code ignore}, + {@code fail} (default for Maven 4 and above) or + {@code warn} (default for Maven 3). + + String + + + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + + + + + + + + + Site + 4.0.0+ + Contains the information needed for deploying websites. + + + id + 4.0.0+ + + A unique identifier for a deployment location. This is used to match the + site to configuration in the {@code settings.xml} file, for example. + + String + + + name + 4.0.0+ + Human readable name of the deployment location. + String + + + url + 4.0.0+ + + Default value is: parent value [+ path adjustment] + (artifactId or project.directory property), or just parent value if + site's {@code child.site.url.inherit.append.path="false"}.

    + ]]> +
    + String +
    + + childSiteUrlInheritAppendPath + 4.0.0+ + + Default value is: {@code true}

    + @since Maven 3.6.1 + ]]> +
    + String +
    +
    + + + 4.0.0+ + + + + + +
    + + + ConfigurationContainer + 4.0.0+ + Contains the configuration information of the container like Plugin. + + + inherited + 4.0.0+ + + Whether any configuration should be propagated to child POMs. Note: While the type + of this field is {@code String} for technical reasons, the semantic type is actually + {@code Boolean}. Default value is {@code true}. + + String + + + + The configuration as DOM object.

    +

    By default, every element content is trimmed, but starting with Maven 3.1.0, you can add + {@code xml:space="preserve"} to elements you want to preserve whitespace.

    +

    You can control how child POMs inherit configuration from parent POMs by adding {@code combine.children} + or {@code combine.self} attributes to the children of the configuration element:

    +
      +
    • {@code combine.children}: available values are {@code merge} (default) and {@code append},
    • +
    • {@code combine.self}: available values are {@code merge} (default) and {@code override}.
    • +
    + + @see POM Reference documentation + @see Xpp3DomUtils + ]]> +
    + configuration + DOM +
    +
    + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + + + + +
    + + Plugin + 4.0.0+ + ConfigurationContainer + + The {@code <plugin>} element contains information required for a plugin. + + + + groupId + The group ID of the plugin in the repository. + 4.0.0+ + String + org.apache.maven.plugins + + + artifactId + The artifact ID of the plugin in the repository. + 4.0.0+ + String + true + + + version + 4.0.0+ + The version (or valid range of versions) of the plugin to be used. + String + + + extensions + 4.0.0+ + String + + Whether to load Maven extensions (such as packaging and type handlers) from + this plugin. For performance reasons, this should only be enabled when necessary. Note: While the type + of this field is {@code String} for technical reasons, the semantic type is actually + {@code Boolean}. Default value is {@code false}. + + + + executions + 4.0.0+ + Multiple specifications of a set of goals to execute during the build + lifecycle, each having (possibly) a different configuration. + + PluginExecution + * + + + + goals + 4.0.0 + + @deprecated Unused by Maven. + + DOM + + @Deprecated(since = "4.0.0") + + + + dependencies + Additional dependencies that this project needs to introduce to the plugin's + classloader. + 4.0.0+ + + Dependency + * + + + + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + getExecutionsAsMap() { + return getExecutions().stream().collect(Collectors.toMap(exec -> exec.getId(), exec -> exec)); + } + ]]> + + + + 4.0.0+ + + ::}, never {@code null} + */ + public String getId() { + return new StringBuilder(128) + .append((getGroupId() == null) ? "[unknown-group-id]" : getGroupId()) + .append(":") + .append((getArtifactId() == null) ? "[unknown-artifact-id]" : getArtifactId()) + .append(":") + .append((getVersion() == null) ? "[unknown-version]" : getVersion()) + .toString(); + } + + /** + * @return the key of the plugin, ie {@code groupId:artifactId} + */ + public String getKey() { + return constructKey(getGroupId(), getArtifactId()); + } + + /** + * @param groupId the group ID of the plugin in the repository + * @param artifactId the artifact ID of the reporting plugin in the repository + * @return the key of the plugin, ie {@code groupId:artifactId} + */ + public static String constructKey(String groupId, String artifactId) { + return groupId + ":" + artifactId; + } + + ]]> + + + + 4.1.0+ + + + + + + + + PluginExecution + 4.0.0+ + ConfigurationContainer + + The {@code @lt;execution>} element contains information required for the + execution of a plugin. + + + + id + 4.0.0+ + String + default + The identifier of this execution for labelling the goals during the build, + and for matching executions to merge during inheritance and profile injection. + + + phase + 4.0.0+ + String + The build lifecycle phase to bind the goals in this execution to. If omitted, + the goals will be bound to the default phase specified by the plugin. + + + priority + 4.0.0+ + int + + Warning: This is an internal utility property that is only public for technical reasons, + it is not part of the public API. In particular, this property can be changed or deleted without prior + notice.

    + ]]> +
    +
    + + goals + 4.0.0+ + The goals to execute with the given configuration. + + String + * + + +
    + + + 4.0.0+ + + + + + +
    + + DependencyManagement + 4.0.0+ + Section for management of default dependency information for use in a group of + POMs. + + + dependencies + 4.0.0+ + The dependencies specified here are not used until they are referenced in a + POM within the group. This allows the specification of a "standard" version for a + particular dependency. + + Dependency + * + + + + + + PluginManagement + 4.0.0+ + PluginContainer + Section for management of default plugin information for use in a group of POMs. + + + + Reporting + 4.0.0+ + Section for management of reports and their configuration. + + + excludeDefaults + 4.0.0+ + String + + If true, then the default reports are not included in the site generation. + This includes the reports in the "Project Info" menu. Note: While the type + of this field is {@code String} for technical reasons, the semantic type is actually + {@code Boolean}. Default value is {@code false}. + + + + outputDirectory + 4.0.0+ + String + + Where to store all the generated reports. The default is + {@code ${project.build.directory}/site}. + + + + + plugins + 4.0.0+ + The reporting plugins to use and their configuration. + + ReportPlugin + * + + + + + + 4.0.0+ + + + + + + 4.0.0/4.0.99 + + reportPluginMap field to null + */ + @Deprecated + public void flushReportPluginMap() { + } + + /** + * @return a Map of plugins field with ReportPlugin#getKey() as key + * @see org.apache.maven.model.ReportPlugin#getKey() + */ + public java.util.Map getReportPluginsAsMap() { + return getPlugins().stream().collect(Collectors.toMap(report -> report.getKey(), report -> report)); + } + ]]> + + + + + + + Profile + ModelBase + 4.0.0+ + Modifications to the build process which is activated based on environmental + parameters or command line arguments. + + + id + true + 4.0.0+ + String + default + The identifier of this build profile. This is used for command line + activation, and identifies profiles to be merged. + + + + activation + 4.0.0+ + The conditional logic which will automatically trigger the inclusion of this + profile. + + Activation + + + + build + 4.0.0+ + true + Information required to build the project. + + BuildBase + + + + + + 4.0.0/4.0.99 + + + + + + 4.1.0+ + + + + + + + + Activation + 4.0.0+ + In addition to the traditional activation mechanisms (JDK version, OS properties, + file existence, etc.), Maven now supports a powerful condition-based activation + through the {@code condition} field. This new mechanism allows for more flexible + and expressive profile activation rules.

    + +

    Condition Syntax

    + +

    The condition is specified as a string expression that can include various + functions, comparisons, and logical operators. Some key features include:

    + +
      +
    • Property access: {@code ${property.name}}
    • +
    • Comparison operators: {@code ==}, {@code !=}, {@code <}, {@code >}, {@code <=}, {@code >=}
    • +
    • Logical operators: {@code &&} (AND), {@code ||} (OR), {@code not(...)}
    • +
    • Functions: {@code exists(...)}, {@code missing(...)}, {@code matches(...)}, {@code inrange(...)}, and more
    • +
    + +

    Supported Functions

    + +

    The following functions are supported in condition expressions:

    + +
      +
    • {@code length(string)}: Returns the length of the given string.
    • +
    • {@code upper(string)}: Converts the string to uppercase.
    • +
    • {@code lower(string)}: Converts the string to lowercase.
    • +
    • {@code substring(string, start, [end])}: Returns a substring of the given string.
    • +
    • {@code indexOf(string, substring)}: Returns the index of the first occurrence of substring in string, or -1 if not found.
    • +
    • {@code contains(string, substring)}: Checks if the string contains the substring.
    • +
    • {@code matches(string, regex)}: Checks if the string matches the given regular expression.
    • +
    • {@code not(condition)}: Negates the given condition.
    • +
    • {@code if(condition, trueValue, falseValue)}: Returns trueValue if the condition is true, falseValue otherwise.
    • +
    • {@code exists(path)}: Checks if a file matching the given glob pattern exists.
    • +
    • {@code missing(path)}: Checks if a file matching the given glob pattern does not exist.
    • +
    • {@code inrange(version, range)}: Checks if the given version is within the specified version range.
    • +
    + +

    Supported properties

    + +

    The following properties are supported in expressions:

    + +
      +
    • `project.basedir`: The project directory
    • +
    • `project.rootDirectory`: The root directory of the project
    • +
    • `project.artifactId`: The artifactId of the project
    • +
    • `project.packaging`: The packaging of the project
    • +
    • user properties
    • +
    • system properties (including environment variables prefixed with `env.`)
    • +
    + +

    Examples

    + +
      +
    • JDK version range: {@code inrange(${java.version}, '[11,)')} (JDK 11 or higher)
    • +
    • OS check: {@code ${os.name} == 'windows'}
    • +
    • File existence: {@code exists('${project.basedir}/src/**}{@code /*.xsd')}
    • +
    • Property check: {@code ${my.property} != 'some-value'}
    • +
    • Regex matching: {@code matches(${os.version}, '.*aws')}
    • +
    • Complex condition: {@code ${os.name} == 'windows' && ${os.arch} != 'amd64' && inrange(${os.version}, '[10,)')}
    • +
    • String length check: {@code length(${user.name}) > 5}
    • +
    • Substring with version: {@code substring(${java.version}, 0, 3) == '1.8'}
    • +
    • Using indexOf: {@code indexOf(${java.version}, '-') > 0}
    • +
    • Conditional logic: {@code if(contains(${java.version}, '-'), substring(${java.version}, 0, indexOf(${java.version}, '-')), ${java.version})}
    • +
    + +

    This flexible condition mechanism allows for more precise control over profile + activation, enabling developers to create profiles that respond to a wide range of + environmental factors and project states.

    + ]]>
    + + + activeByDefault + 4.0.0+ + boolean + If set to true, this profile will be active unless another profile in this + pom is activated using the command line -P option or by one of that profile's + activators. + + + jdk + 4.0.0+ + String + + Specifies that this profile will be activated when a matching JDK is detected. + For example, {@code 1.4} only activates on JDKs versioned 1.4, + while {@code !1.4} matches any JDK that is not version 1.4. Ranges are supported too: + {@code [1.5,)} activates when the JDK is 1.5 minimum. + + + + os + 4.0.0+ + Specifies that this profile will be activated when matching operating system + attributes are detected. + + ActivationOS + + + + property + 4.0.0+ + Specifies that this profile will be activated when this property is + specified. + + ActivationProperty + + + + file + 4.0.0+ + Specifies that this profile will be activated based on existence of a file. + + ActivationFile + + + + packaging + 4.1.0+ + String + Specifies that this profile will be activated based on the project's packaging. + + + condition + 4.1.0+ + String + The condition which must be satisfied to activate the profile. + + + +
    + + ActivationProperty + 4.0.0+ + This is the property specification used to activate a profile. If the value field + is empty, then the existence of the named property will activate the profile, otherwise it + does a case-sensitive match against the property value as well. + + + name + 4.0.0+ + String + true + The name of the property to be used to activate a profile. + + + value + 4.0.0+ + String + The value of the property required to activate a profile. + + + + + ActivationOS + 4.0.0+ + This is an activator which will detect an operating system's attributes in order + to activate its profile. + + + name + 4.0.0+ + String + + The name of the operating system to be used to activate the profile. This must be an exact match + of the {@code ${os.name}} Java property, such as {@code Windows XP}. + + + + family + 4.0.0+ + String + + The general family of the OS to be used to activate the profile, such as + {@code windows} or {@code unix}. + + + + arch + 4.0.0+ + String + The architecture of the operating system to be used to activate the + profile. + + + version + 4.0.0+ + String + The version of the operating system to be used to activate the + profile. + + + + + ActivationFile + 4.0.0+ + This is the file specification used to activate the profile. The {@code missing} value + is the location of a file that needs to exist, and if it doesn't, the profile will be + activated. On the other hand, {@code exists} will test for the existence of the file and if it is + there, the profile will be activated. + <p>Variable interpolation for these file specifications is limited to {@code ${project.basedir}}, + system properties and user properties.</p> + + + missing + 4.0.0+ + String + The name of the file that must be missing to activate the profile. Please note, that missing and exists + fields cannot be used together. Only one of them should be used at any one time. + + + exists + 4.0.0+ + String + The name of the file that must exist to activate the profile. Please note, that missing and exists + fields cannot be used together. Only one of them should be used at any one time. + + + + + + + ReportPlugin + 4.0.0+ + ConfigurationContainer + + } element in {@code } contains information required for a report plugin. + ]]> + + + + groupId + 4.0.0+ + String + true + org.apache.maven.plugins + The group ID of the reporting plugin in the repository. + + + artifactId + 4.0.0+ + String + true + The artifact ID of the reporting plugin in the repository. + + + version + 4.0.0+ + + The version of the reporting plugin to be used. Starting with Maven 3, if no version is defined explicitly, + version is searched in {@code build/plugins} then in {@code build/pluginManagement}. + + String + + + reportSets + 4.0.0+ + + Multiple specifications of a set of reports, each having (possibly) different + configuration. This is the reporting parallel to an {@code execution} in the build. + + + ReportSet + * + + + + + + 4.0.0+ + + reportSetMap = null; + + /** + * Reset the {@code reportSetMap} field to {@code null} + */ + public void flushReportSetMap() { + this.reportSetMap = null; + } + + /** + * @return a Map of reportSets field with {@code ReportSet#getId()} as key + * @see ReportSet#getId() + */ + public java.util.Map getReportSetsAsMap() { + if (reportSetMap == null) { + reportSetMap = new java.util.LinkedHashMap(); + if (getReportSets() != null) { + for (java.util.Iterator i = getReportSets().iterator(); i.hasNext(); ) { + ReportSet reportSet = (ReportSet) i.next(); + reportSetMap.put(reportSet.getId(), reportSet); + } + } + } + + return reportSetMap; + } + + /** + * @return the key of the report plugin, ie {@code groupId:artifactId} + */ + public String getKey() { + return constructKey(getGroupId(), getArtifactId()); + } + + /** + * @param groupId The group ID of the plugin in the repository + * @param artifactId The artifact ID of the reporting plugin in the repository + * @return the key of the report plugin, ie {@code groupId:artifactId} + */ + public static String constructKey(String groupId, String artifactId) { + return groupId + ":" + artifactId; + } + ]]> + + + + + + ReportSet + 4.0.0+ + ConfigurationContainer + Represents a set of reports and configuration to be used to generate them. + + + id + String + true + The unique id for this report set, to be used during POM inheritance and profile injection + for merging of report sets. + + default + + + reports + 4.0.0+ + true + The list of reports from this plugin which should be generated from this set. + + String + * + + + + + + 4.0.0+ + + + + + + + + Prerequisites + 4.0.0+ + Describes the prerequisites a project can have. + + + maven + 4.0.0+ + String + 2.0 + + + false + + + + + Relocation + 4.0.0+ + Describes where an artifact has moved to. If any of the values are omitted, it is + assumed to be the same as it was before. + + + groupId + 4.0.0+ + The group ID the artifact has moved to. + String + + + artifactId + 4.0.0+ + The new artifact ID of the artifact. + String + + + version + 4.0.0+ + The new version of the artifact. + String + + + message + 4.0.0+ + An additional message to show the user about the move, such as the reason. + String + + + + + Extension + 4.0.0+ + Describes a build extension to utilise. + + + groupId + 4.0.0+ + The group ID of the extension's artifact. + true + String + + + artifactId + 4.0.0+ + The artifact ID of the extension. + true + String + + + version + 4.0.0+ + The version of the extension. + String + + + configuration + 4.1.0+ + + The configuration of the extension. + @since Maven 4.0.0 + + DOM + + + + + 4.1.0+ + + + + + + + + InputLocation + 4.0.0/4.0.99 + + + + + + 4.0.0+ + + + + + + + + InputSource + 4.0.0/4.0.99 + + + modelId + 4.0.0+ + String + + The identifier of the POM in the format {@code <groupId>:<artifactId>:<version>}. + + + + location + 4.0.0+ + String + + The path/URL of the POM or {@code null} if unknown. + + + + + + 4.0.0+ + + + + + + +
    +
    diff --git a/api/maven-api-model/src/site/apt/index.apt b/api/maven-api-model/src/site/apt/index.apt new file mode 100644 index 000000000000..e64b4fb211a9 --- /dev/null +++ b/api/maven-api-model/src/site/apt/index.apt @@ -0,0 +1,36 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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 +~~ +~~ http://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. + + ----- + Introduction + ----- + Jason van Zyl + Vincent Siveton + Hervé Boutemy + ----- + 2011-06-12 + ----- + +Maven 4 API - Immutable Maven Model + + This is strictly the immutable model for Maven POM (Project Object Model), so really just plain objects in <<>> package. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with <<>> inner classes for immutable instances creation. + + See also corresponding {{{../../maven-model/index.html}Maven classical POM model documentation}}. diff --git a/api/maven-api-model/src/site/site.xml b/api/maven-api-model/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-model/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/maven-api-plugin/pom.xml b/api/maven-api-plugin/pom.xml new file mode 100644 index 000000000000..2e9c1706edaa --- /dev/null +++ b/api/maven-api-plugin/pom.xml @@ -0,0 +1,114 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-plugin + + Maven 4 API :: Plugin + Maven 4 API - Immutable Plugin model. + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-xml + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.codehaus.modello + modello-maven-plugin + + + modello-plugin + + velocity + xdoc + xsd + + generate-sources + + ${project.basedir}/../../src/mdo + 2.0.0 + + src/main/mdo/plugin.mdo + + + + + + packageModelV4=org.apache.maven.api.plugin.descriptor + + + + + modello-lifecycle + + velocity + xdoc + xsd + + generate-sources + + ${project.basedir}/../../src/mdo + 2.0.0 + + src/main/mdo/lifecycle.mdo + + + + + + packageModelV4=org.apache.maven.api.plugin.descriptor.lifecycle + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/package-info.java + + + + + + + diff --git a/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java b/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java new file mode 100644 index 000000000000..28a7936fed4c --- /dev/null +++ b/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides annotations for Maven plugin development, including mojo configuration, + * parameter definitions, and lifecycle bindings. These annotations are used to + * generate plugin descriptors and configure plugin behavior. + * + * @since 4.0.0 + */ +package org.apache.maven.api.plugin.annotations; diff --git a/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/descriptor/lifecycle/package-info.java b/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/descriptor/lifecycle/package-info.java new file mode 100644 index 000000000000..800376de87fc --- /dev/null +++ b/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/descriptor/lifecycle/package-info.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Contains classes for managing plugin-specific lifecycle bindings and forked executions. + * This package helps define how plugins can modify or extend Maven's build lifecycle. + * + * @since 4.0.0 + */ +package org.apache.maven.api.plugin.descriptor.lifecycle; diff --git a/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/descriptor/package-info.java b/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/descriptor/package-info.java new file mode 100644 index 000000000000..63d55f5447b8 --- /dev/null +++ b/api/maven-api-plugin/src/main/java/org/apache/maven/api/plugin/descriptor/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides classes for Maven plugin descriptors that define plugin metadata, configuration, + * and execution parameters. These descriptors are typically stored in plugin.xml files + * within the META-INF/maven directory of plugin JARs. + * + * @since 4.0.0 + */ +package org.apache.maven.api.plugin.descriptor; diff --git a/api/maven-api-plugin/src/main/mdo/lifecycle.mdo b/api/maven-api-plugin/src/main/mdo/lifecycle.mdo new file mode 100644 index 000000000000..c7e4539adbf6 --- /dev/null +++ b/api/maven-api-plugin/src/main/mdo/lifecycle.mdo @@ -0,0 +1,162 @@ + + + + lifecycle + Lifecycle + + Configuration of custom lifecycle mappings for the plugin, as generally stored in + {@code META-INF/maven/lifecycle.xml} in a plugin's jar artifact. + + + + LifecycleConfiguration + 1.0.0+ + Root element of the {@code lifecycle.xml} file. + + + lifecycles + 1.0.0+ + + Lifecycle + * + + + + + + Lifecycle + 1.0.0+ + A custom lifecycle mapping definition. + + + id + true + 1.0.0+ + String + The ID of this lifecycle, for identification in the mojo descriptor. + + + phases + 1.0.0+ + The phase mappings for this lifecycle. + + Phase + * + + + + + + Phase + 1.0.0+ + A phase mapping definition. + + + id + true + 1.0.0+ + String + The ID of this phase, e.g., {@code generate-sources}. + + + executionPoint + false + 2.0.0+ + String + + + + + priority + false + 2.0.0+ + int + 0 + If specified, identifies a within phase prioritization of executions. + + + executions + 1.0.0+ + The goals to execute within the phase. + + Execution + * + + + + configuration + 1.0.0+ + DOM + Configuration to pass to all goals run in this phase. + + + + + 2.0.0+ + + + + + + Execution + 1.0.0+ + A set of goals to execute. + + + configuration + 1.0.0+ + DOM + Configuration to pass to the goals. + + + goals + 1.0.0+ + The goals to execute. + + String + * + + + + + + diff --git a/api/maven-api-plugin/src/main/mdo/plugin.mdo b/api/maven-api-plugin/src/main/mdo/plugin.mdo new file mode 100644 index 000000000000..5eeaaf331573 --- /dev/null +++ b/api/maven-api-plugin/src/main/mdo/plugin.mdo @@ -0,0 +1,617 @@ + + + + plugin + PluginDescriptor + An XSD is available at https://maven.apache.org/xsd/plugin-2.0.0.xsd

    + ]]>
    + + + PluginDescriptor + 1.0.0+ + Root element of the {@code plugin.xml} file. + + + name + 1.0.0+ + Name of the plugin. + String + + + description + 1.0.0+ + Description of the plugin. + String + + + groupId + 1.0.0+ + The group id of the plugin. + String + true + + + artifactId + 1.0.0+ + The artifact id of the plugin. + String + + + version + 1.0.0+ + The version of the plugin. + String + true + + + goalPrefix + 1.0.0+ + + String + + + isolatedRealm + 1.0.0+ + + boolean + false + + + inheritedByDefault + 1.0.0+ + + boolean + true + + + requiredJavaVersion + 1.1.0+ + + A version range which specifies the supported Java versions. A version range can either use the usual mathematical syntax "[2.0.10,2.1.0),[3.0,)" or use a single version "2.2.1". The latter is a short form for "[2.2.1,)", i.e. denotes the minimum version required. + @since Maven 4.0.0-alpha-3 + + String + + + requiredMavenVersion + 1.1.0+ + + A version range which specifies the supported Maven versions. A version range can either use the usual mathematical syntax "[2.0.10,2.1.0),[3.0,)" or use a single version "2.2.1". The latter is a short form for "[2.2.1,)", i.e. denotes the minimum version required. This value takes precedence over the POMs Maven prerequisite. + @since Maven 4.0.0-alpha-3 + + String + + + mojos + 1.0.0+ + + MojoDescriptor + * + + Description of each Mojo provided by the plugin. + + + dependencies + 1.0.0/1.1.0 + + Dependency + * + + + A set of dependencies which the plugin requires in order to function. This enables the plugin to function + independently of its POM (or at least to declare the libraries it needs to run). + + + + + + 2.0.0+ + + + + + + + MojoDescriptor + 1.0.0+ + A Mojo description. + + + goal + true + 1.0.0+ + String + + The goal name for the Mojo, that users will reference from the command line to execute the Mojo directly, + or inside a POM in order to provide Mojo-specific configuration. + + + + description + 1.0.0+ + String + The description of this Mojo's functionality. + + + implementation + 1.0.0+ + String + + The Mojo's fully-qualified class name (or script path in the case of non-Java Mojos). + + + + language + 1.0.0+ + String + java + The implementation language for this Mojo (java, beanshell, etc.). + + + phase + 1.0.0+ + String + + Defines a default phase to bind a Mojo execution to if the user does not explicitly set a phase in the POM. + <p><b>Note:</b> This will not automagically make a Mojo run when the plugin declaration is added + to the POM. It merely enables the user to omit the {@code <phase>} element from the + surrounding {@code <execution>} element.</p> + + + + executePhase + 1.0.0+ + String + Reference the invocation phase of the Mojo. + + + executeGoal + 1.0.0+ + String + Reference the invocation goal of the Mojo. + + + executeLifecycle + 1.0.0+ + String + + + + requiresDependencyResolution + 1.0.0/1.1.0 + String + + Flags this Mojo as requiring the dependencies in the specified class path to be resolved before it can + execute: {@code compile}, {@code runtime}, {@code test}, + {@code compile+runtime} (since Maven 3.0) or {@code runtime+system} (since Maven 3.0) + + + + dependencyResolution + 2.0.0+ + String + + Flags this Mojo as requiring the dependencies in the specified class path to be resolved before it can + execute: {@code compile}, {@code runtime}, {@code test}, + {@code compile+runtime} (since Maven 3.0) or {@code runtime+system} (since Maven 3.0) + + + + requiresDependencyCollection + 1.0.0/1.1.0 + String + + Flags this Mojo as requiring information about the dependencies that would make up the specified class + path. As the name suggests, this is similar to requiresDependencyResolution and supports the same values. + The important difference is this will not resolve the files for the dependencies, i.e. the artifacts + associated with a Maven project can lack a file. As such, this annotation is meant for Mojos that only + want to analyze the set of transitive dependencies, in particular during early lifecycle phases where + full dependency resolution might fail due to projects which haven't been built yet. + + + + dependencyCollection + 2.0.0+ + String + + Flags this Mojo as requiring information about the dependencies that would make up the specified class + path. As the name suggests, this is similar to requiresDependencyResolution and supports the same values. + The important difference is this will not resolve the files for the dependencies, i.e. the artifacts + associated with a Maven project can lack a file. As such, this annotation is meant for Mojos that only + want to analyze the set of transitive dependencies, in particular during early lifecycle phases where + full dependency resolution might fail due to projects which haven't been built yet. + + + + requiresDirectInvocation + 1.0.0/1.1.0 + boolean + Flags this Mojo to be invoked directly only. + false + + + directInvocationOnly + 2.0.0+ + boolean + Flags this Mojo to be invoked directly only. + false + + + requiresProject + 1.0.0/1.1.0 + boolean + Flags this Mojo to require running inside of a project. + true + + + projectRequired + 2.0.0+ + boolean + Flags this Mojo to require running inside of a project. + true + + + requiresReports + 1.0.0 + boolean + Flags this Mojo to require running inside of a reports context. Unsupported since Maven 3.0. + false + + + requiresOnline + 1.0.0/1.1.0 + boolean + Flags this Mojo to require online mode for its operation. + false + + + onlineRequired + 2.0.0+ + boolean + Flags this Mojo to require online mode for its operation. + false + + + aggregator + 1.0.0+ + boolean + + Flags this Mojo to run it in a multi-module way, i.e. aggregate the build with the set of projects + listed as modules. + + false + + + inheritedByDefault + 1.0.0+ + boolean + Specify that the Mojo is inherited. + true + + + threadSafe + 1.0.0/1.1.0 + boolean + + Marks this Mojo as being thread-safe, i.e. the Mojo safely supports concurrent execution during parallel + builds. Mojos without this annotation will make Maven output a warning when used during a parallel build + session. + @since Maven 3.0. + + false + + + v4Api + 1.1.0 + boolean + Marks this Mojo as using Maven 4 API. This makes the plugin implicitly incompatible with earlier Maven versions. Only evaluated since Maven 4. + false + + + instantiationStrategy + 1.0.0/1.1.0 + String + per-lookup + Specify the instantiation strategy. + + + executionStrategy + 1.0.0/1.1.0 + String + + Specify the execution strategy: {@code once-per-session}, {@code always}. + + once-per-session + + + since + 1.0.0+ + String + Specify the version when the Mojo was added to the API. Similar to Javadoc since. + + + deprecated + 1.0.0+ + String + + Description with the reason of Mojo deprecation. Similar to Javadoc {@code @deprecated} + This will trigger a warning when a user tries to use a Mojo marked as deprecated. + + + + configurator + 1.0.0+ + String + + The configurator type to use when injecting parameter values into this Mojo. The value is normally deduced + from the Mojo's implementation language, but can be specified to allow a custom ComponentConfigurator + implementation to be used. + + + + composer + 1.0.0/1.1.0 + String + + + + parameters + 1.0.0+ + + + Parameter + * + + + + resolutions + 2.0.0+ + + + Resolution + * + + + + requirements + 1.0.0/1.1.0 + Use Maven 4 Dependency Injection (for v4 plugins) or JSR 330 annotations (for v3 plugins) to inject dependencies instead. + + Requirement + * + + + + id + 2.0.0+ + String + the id of the mojo, based on the goal name + + + fullGoalName + 2.0.0+ + String + the full goal name + + + + + + Parameter + 1.0.0+ + A parameter description. + + + + name + 1.0.0+ + String + true + + The name of the parameter, to be used while configuring this parameter from the Mojo's declared defaults + or from the POM. + + + + alias + 1.0.0+ + String + + Specifies an alias which can be used to configure this parameter from the POM. + This is primarily useful to improve user-friendliness, where Mojo field names are not intuitive to the + user or are otherwise not conducive to configuration via the POM. + + + + type + 1.0.0+ + String + true + + The Java type for this parameter. This is used to validate the result of any expressions used to calculate + the value which should be injected into the Mojo for this parameter. + + + + required + 1.0.0+ + boolean + + Whether this parameter is required for the Mojo to function. This is used to validate the configuration + for a Mojo before it is injected, and before the Mojo is executed from some half-state. + + + + editable + 1.0.0+ + boolean + true + + Specifies that this parameter can be configured directly by the user (as in the case of POM-specified + configuration). This is useful when you want to force the user to use common POM elements rather than + plugin configurations, as in the case where you want to use the artifact's final name as a parameter. In + this case, you want the user to modify {@code <build><finalName/></build>} rather + than specifying a value for finalName directly in the plugin configuration section. It is also useful to + ensure that - for example - a List-typed parameter which expects items of type Artifact doesn't get a List + full of Strings. + + + + description + 1.0.0+ + String + The description of this parameter's use inside the Mojo. + + + since + 1.0.0+ + String + Specify the version when the parameter was added to the API. Similar to Javadoc since. + + + deprecated + 1.0.0+ + String + + Description with the reason of parameter deprecation. Similar to Javadoc {@code @deprecated}. + This will trigger a warning when a user tries to configure a parameter marked as deprecated. + + + + expression + 2.0.0+ + String + Parameter expression, to let user override default value with a user property, system property or project property. + + + defaultValue + 2.0.0+ + String + The default value, as an expression that will be evaluated at injection or run-time. + + + + + + Requirement + 1.0.0/1.1.0 + Describes a component requirement. + + + + role + true + String + + + + roleHint + String + + + + fieldName + true + String + The field name which has this requirement. + + + + + + Dependency + 1.0.0/1.1.0 + Definition of a dependency, needed by the plugin at runtime. + + + groupId + true + 1.0.0+ + String + The group id of the dependency. + + + artifactId + true + 1.0.0+ + String + The artifact id of the dependency. + + + version + 1.0.0+ + String + The version of the dependency. + + + type + 1.0.0+ + String + jar + The type of dependency. + + + + + + Resolution + 2.0.0+ + Dependency collection or resolution injection. + + + field + false + 2.0.0+ + String + the name of the field to be injected + + + pathScope + false + 2.0.0+ + String + pathScope used to flatten dependencies + + + requestType + false + 2.0.0+ + String + either {@code collect}, {@code flatten}, or {@code resolve} + + + + +
    diff --git a/api/maven-api-plugin/src/site/apt/index.apt b/api/maven-api-plugin/src/site/apt/index.apt new file mode 100644 index 000000000000..f229c29d8a8e --- /dev/null +++ b/api/maven-api-plugin/src/site/apt/index.apt @@ -0,0 +1,33 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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 +~~ +~~ http://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. + + ----- + Introduction + ----- + Guillaume Nodet + ----- + 2023-11-15 + ----- + +Maven 4 API - Plugin Descriptor Model + + This is the immutable model for Maven Plugin Descriptor in <<>> package. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with <<>> inner classes for immutable instances creation. + diff --git a/api/maven-api-plugin/src/site/site.xml b/api/maven-api-plugin/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-plugin/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/maven-api-plugin/src/test/java/org/apache/maven/api/plugin/descriptor/another/ExtendedPluginDescriptorTest.java b/api/maven-api-plugin/src/test/java/org/apache/maven/api/plugin/descriptor/another/ExtendedPluginDescriptorTest.java new file mode 100644 index 000000000000..c318cb815f44 --- /dev/null +++ b/api/maven-api-plugin/src/test/java/org/apache/maven/api/plugin/descriptor/another/ExtendedPluginDescriptorTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.plugin.descriptor.another; + +import org.apache.maven.api.plugin.descriptor.PluginDescriptor; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Verifies that subclasses from generated model classes are possible. + */ +class ExtendedPluginDescriptorTest { + + /** + * A subclass of the generated class {@link PluginDescriptor} that adds an additional field. + */ + static class ExtendedPluginDescriptor extends PluginDescriptor { + + private final String additionalField; + + ExtendedPluginDescriptor(Builder builder) { + super(builder); + this.additionalField = builder.additionalField; + } + + public String getAdditionalField() { + return additionalField; + } + + static class Builder extends PluginDescriptor.Builder { + protected String additionalField; + + Builder() { + super(false); + } + + public Builder additionalField(String additionalField) { + this.additionalField = additionalField; + return this; + } + + @Override + public ExtendedPluginDescriptor build() { + return new ExtendedPluginDescriptor(this); + } + } + } + + @Test + void testExtendedPluginDescriptor() { + ExtendedPluginDescriptor.Builder builder = new ExtendedPluginDescriptor.Builder(); + // make sure to call the subclasses' builder methods first, otherwise fluent API would not work + builder.additionalField("additional") + .groupId("org.apache.maven") + .artifactId("maven-plugin-api") + .version("1.0.0"); + ExtendedPluginDescriptor descriptor = builder.build(); + assertEquals("additional", descriptor.getAdditionalField()); + assertEquals("org.apache.maven", descriptor.getGroupId()); + } +} diff --git a/api/maven-api-settings/pom.xml b/api/maven-api-settings/pom.xml new file mode 100644 index 000000000000..a73c2ce1de0d --- /dev/null +++ b/api/maven-api-settings/pom.xml @@ -0,0 +1,113 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-settings + + Maven 4 API :: Settings + Maven 4 API - Immutable Settings model. + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-xml + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.codehaus.modello + modello-maven-plugin + + 2.0.0 + ${project.basedir}/../../src/mdo + + src/main/mdo/settings.mdo + + + + + + packageModelV4=org.apache.maven.api.settings + locationTracking=true + generateLocationClasses=true + + + + + modello + + velocity + xdoc + xsd + + generate-resources + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/package-info.java + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + **/package-info.java + + + **/*.java + + false + true + + + + + diff --git a/api/maven-api-settings/src/main/java/org/apache/maven/api/settings/package-info.java b/api/maven-api-settings/src/main/java/org/apache/maven/api/settings/package-info.java new file mode 100644 index 000000000000..14782d443861 --- /dev/null +++ b/api/maven-api-settings/src/main/java/org/apache/maven/api/settings/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Contains classes for managing Maven settings, including global and user-specific + * configuration stored in settings.xml files. These settings control repository access, + * authentication, proxies, and other Maven runtime behaviors. + * + * @since 4.0.0 + */ +package org.apache.maven.api.settings; diff --git a/api/maven-api-settings/src/main/mdo/settings.mdo b/api/maven-api-settings/src/main/mdo/settings.mdo new file mode 100644 index 000000000000..fe69efeeff16 --- /dev/null +++ b/api/maven-api-settings/src/main/mdo/settings.mdo @@ -0,0 +1,1051 @@ + + + + + + settings + Settings + + This is a reference for the user-specific configuration for Maven.

    +

    Includes things that should not be distributed with the pom.xml file, such as developer identity, along with + local settings, like proxy information.

    +

    The default location for the settings file is {@code ~/.m2/settings.xml}.

    + ]]> +
    + + + package + org.apache.maven.settings + + + + + TrackableBase + 1.0.0+ + + Common base class that contains code to track the source for this instance. + + + + 1.0.0/1.3.0 + + public static final String USER_LEVEL = "user-level"; + public static final String PROJECT_LEVEL = "project-level"; + public static final String GLOBAL_LEVEL = "global-level"; + + private String sourceLevel = USER_LEVEL; + private boolean sourceLevelSet = false; + + public void setSourceLevel(String sourceLevel) { + if (sourceLevelSet) { + throw new IllegalStateException("Cannot reset sourceLevel attribute; it is already set to: " + sourceLevel); + } else if (!(USER_LEVEL.equals(sourceLevel) || PROJECT_LEVEL.equals(sourceLevel) || GLOBAL_LEVEL.equals(sourceLevel))) { + throw new IllegalArgumentException("sourceLevel must be one of: {" + USER_LEVEL + "," + PROJECT_LEVEL + "," + GLOBAL_LEVEL + "}"); + } else { + this.sourceLevel = sourceLevel; + this.sourceLevelSet = true; + } + } + + public String getSourceLevel() { + return sourceLevel; + } + + + + + + IdentifiableBase + TrackableBase + 1.0.0+ + + Base class for {@code Mirror}, {@code Profile}, {@code Proxy} and {@code Server}. + + + + id + 1.0.0+ + String + default + true + Item identifier. + + + + + Settings + 1.0.0+ + TrackableBase + + Root element of the user configuration file. + + + + localRepository + 1.0.0+ + true + + Default value is: {@code ${user.home}/.m2/repository} + ]]> + + String + + + interactiveMode + 1.0.0+ + + Whether Maven should attempt to interact with the user for input. + + boolean + true + + + usePluginRegistry + 1.0.0+ + + Whether Maven should use the {@code plugin-registry.xml} file to manage plugin versions. + + boolean + false + + + offline + 1.0.0+ + false + + Indicate whether maven should operate in offline mode full-time. + + boolean + false + + + proxies + 1.0.0+ + + Configuration for different proxy profiles. Multiple proxy profiles + might come in handy for anyone working from a notebook or other + mobile platform, to enable easy switching of entire proxy + configurations by simply specifying the profile id, again either from + the command line or from the defaults section below. + + + Proxy + * + + + + servers + 1.0.0+ + + Configuration of server-specific settings, mainly authentication + method. This allows configuration of authentication on a per-server + basis. + + + Server + * + + + + mirrors + 1.0.0+ + + Configuration of download mirrors for repositories. + + + Mirror + * + + + + repositories + 1.3.0+ + + The lists of the remote repositories. + + + Repository + * + + + + pluginRepositories + 1.3.0+ + + The lists of the remote repositories for discovering plugins. + + + Repository + * + + + This may be removed or relocated in the near + future. It is undecided whether plugins really need a remote + repository set of their own. + + + + profiles + 1.0.0+ + + Configuration of build profiles for adjusting the build + according to environmental parameters. + + + Profile + * + + + + activeProfiles + 1.0.0+ + + List of manually-activated build profiles, specified in the order in which + they should be applied. + + + String + * + + + + pluginGroups + 1.0.0+ + + List of groupIds to search for a plugin when that plugin + groupId is not explicitly provided. + + + String + * + + + + + + 1.0.0/1.3.0 + + proxies = getProxies(); + if (proxies != null && !proxies.isEmpty()) { + for (Proxy proxy : proxies) { + if (proxy.isActive()) { + activeProxy = proxy; + break; + } + } + } + } + return activeProxy; + } + + public Server getServer(String serverId) { + Server match = null; + java.util.List servers = getServers(); + if (servers != null && serverId != null) { + for (Server server : servers) { + if (serverId.equals(server.getId())) { + match = server; + break; + } + } + } + return match; + } + + @Deprecated + public Mirror getMirrorOf(String repositoryId) { + Mirror match = null; + java.util.List mirrors = getMirrors(); + if (mirrors != null && repositoryId != null) { + for (Mirror mirror : mirrors) { + if (repositoryId.equals(mirror.getMirrorOf())) { + match = mirror; + break; + } + } + } + return match; + } + + private java.util.Map profileMap; + + /** + * Reset the {@code profileMap} field to {@code null} + */ + public void flushProfileMap() { + this.profileMap = null; + } + + /** + * @return a Map of profiles field keyed by {@link Profile#getId()} + */ + public java.util.Map getProfilesAsMap() { + if (profileMap == null) { + profileMap = new java.util.LinkedHashMap(); + if (getProfiles() != null) { + for (Profile profile : getProfiles()) { + profileMap.put(profile.getId(), profile); + } + } + } + return profileMap; + } + + public void setModelEncoding(String modelEncoding) { + update(getDelegate().with().modelEncoding(modelEncoding).build()); + } + ]]> + + + + + + Proxy + 1.0.0+ + IdentifiableBase + + The {@code <proxy>} element contains information required to a proxy settings. + + + + activeString + 1.0.0+ + false + true + + Whether this proxy configuration is the active one. Note: While the type of this field + is {@code String} for technical reasons, the semantic type is actually {@code boolean}. + @see #isActive() + + String + + + protocol + 1.0.0+ + + The proxy protocol. + + String + http + + + username + 1.0.0+ + + The proxy user. + + String + + + password + 1.0.0+ + + The proxy password. + + String + + + portString + 1.0.0+ + + The proxy port. Note: While the type of this field is {@code String} for technical + reasons, the semantic type is actually {@code int}. + @see #getPort() + + String + 8080 + + + host + 1.0.0+ + + The proxy host. + + String + true + + + nonProxyHosts + 1.0.0+ + + The list of non-proxied hosts (delimited by {@code |}). + + String + + + + + 1.0.0/1.3.0 + + public boolean isActive() { + return (getActiveString() != null) ? Boolean.parseBoolean(getActiveString()) : true; + } + + public void setActive(boolean active) { + setActiveString(String.valueOf(active)); + } + + public int getPort() { + return (getPortString() != null) ? Integer.parseInt(getPortString()) : 8080; + } + + public void setPort(int port) { + setPortString(String.valueOf(port)); + } + + + + 2.0.0+ + + /** + * Indicates if this proxy is active. + * To allow interpolation of this field, this method lazily parses + * the {@link #getActiveString()} value as a boolean and defaults to {@code true} + * if not set. + * + * @return a boolean indicating if this proxy is active + */ + public boolean isActive() { + return (getActiveString() != null) ? Boolean.parseBoolean(getActiveString()) : true; + } + + /** + * Returns the port to use for this proxy. + * To allow interpolation of this field, this method lazily parses + * the {@link #getPortString()} value as an integer and defaults to {@code 8080} + * if not set. + * + * @return an integer indicating the port to use for this proxy + */ + public int getPort() { + return (getPortString() != null) ? Integer.parseInt(getPortString()) : 8080; + } + + + + + + + Server + 1.0.0+ + IdentifiableBase + + The {@code <server>} element contains information required to a server settings. + + + + username + 1.0.0+ + + The username used to authenticate. + + String + + + password + 1.0.0+ + + The password used in conjunction with the username to authenticate. + + String + + + privateKey + 1.0.0+ + + The private key location used to authenticate. + + String + + + passphrase + 1.0.0+ + + The passphrase used in conjunction with the privateKey to authenticate. + + String + + + filePermissions + 1.0.0+ + + The permissions for files when they are created. + + String + + + directoryPermissions + 1.0.0+ + + The permissions for directories when they are created. + + String + + + configuration + DOM + + Extra configuration for the transport layer. + + + + + + Mirror + 1.0.0+ + IdentifiableBase + + A download mirror for a given repository. + + + + mirrorOf + true + 1.0.0+ + String + + central or *,!repo1. + *, external:* and external:http:* (since Maven 3.8.0) have + a special meaning: see Mirror Settings guide. + ]]> + + + + name + false + 1.0.0+ + String + + The optional name that describes the mirror. + + + + url + true + 1.0.0+ + String + The URL of the mirror repository. + + + layout + 1.1.0+ + String + default + The layout of the mirror repository. + @since Maven 3. + + + mirrorOfLayouts + 1.1.0+ + String + default,legacy + + The layouts of repositories being mirrored. This value can be used to restrict the usage + of the mirror to repositories with a matching layout (apart from a matching id). + @since Maven 3. + + + + blocked + 1.2.0+ + boolean + false + + Default value is: false +
    Since: Maven 3.8.0 + ]]> +
    +
    +
    + + + 1.0.0+ + + + + + +
    + + + Profile + 1.0.0+ + IdentifiableBase + + Modifications to the build process which is keyed on some sort of environmental parameter. + + + + activation + 1.0.0+ + + The conditional logic which will automatically trigger the inclusion of this profile. + + + Activation + + + + properties + + Extended configuration specific to this profile goes here. + Contents take the form of {@code <property.name>property.value</property.name>} + + Properties + + String + * + + + + repositories + 1.0.0+ + + The lists of the remote repositories. + + + Repository + * + + + + pluginRepositories + 1.0.0+ + + The lists of the remote repositories for discovering plugins. + + + Repository + * + + + This may be removed or relocated in the near + future. It is undecided whether plugins really need a remote + repository set of their own. + + + + + + Activation + 1.0.0+ + + The conditions within the build runtime environment which will trigger + the automatic inclusion of the parent build profile. + + + + activeByDefault + 1.0.0+ + boolean + + Flag specifying whether this profile is active as a default. + + + + jdk + 1.0.0+ + String + + Specifies that this profile will be activated when a matching JDK is detected. + + + + os + 1.0.0+ + + Specifies that this profile will be activated when matching OS attributes are detected. + + + ActivationOS + + + + property + 1.0.0+ + + Specifies that this profile will be activated when this property is specified. + + + ActivationProperty + + + + file + 1.0.0+ + + Specifies that this profile will be activated based on existence of a file. + + + ActivationFile + + + + packaging + 1.2.0+ + String + + Specifies that this profile will be activated based on the project's packaging. + + + + condition + 2.0.0+ + String + + The condition which must be satisfied to activate the profile. + + + + + + + + RepositoryBase + 1.0.0+ + IdentifiableBase + + Repository contains the information needed + for establishing connections with remote repository + + + + name + 1.0.0+ + + Human readable name of the repository. + + String + + + url + 1.0.0+ + + The url of the repository. + + String + + + layout + 1.0.0+ + + The type of layout this repository uses for locating and + storing artifacts - can be "legacy" or "default". + + String + default + + + + + 1.0.0/1.1.0 + + + + + + + + + Repository + RepositoryBase + 1.0.0+ + + Repository contains the information needed for establishing + connections with remote repository + + + + releases + 1.0.0+ + + How to handle downloading of releases from this repository + + + RepositoryPolicy + + + + snapshots + 1.0.0+ + + How to handle downloading of snapshots from this repository + + + RepositoryPolicy + + + + + + + 1.0.0/1.1.0 + + + + + + + + + RepositoryPolicy + 1.0.0+ + Download policy + + + enabled + 1.0.0+ + + Whether to use this repository for downloading this type of + artifact. + + boolean + true + + + updatePolicy + 1.0.0+ + + The frequency for downloading updates - can be "always", + "daily" (default), "interval:XXX" (in minutes) or "never" + (only if it doesn't exist locally). + + String + + + checksumPolicy + 1.0.0+ + + What to do when verification of an artifact checksum fails. Valid values are "fail" (default for Maven 4 and + above), "warn" (default for Maven 3) or "ignore". + + String + + + + + + ActivationProperty + 1.0.0+ + + This is the property specification used to activate a profile. If the value field is empty, + then the existence of the named property will activate the profile, otherwise it does a case-sensitive + match against the property value as well. + + + + name + 1.0.0+ + String + true + + The name of the property to be used to activate a profile. + + + + value + 1.0.0+ + String + + The value of the property to be used to activate a profile. + + + + + + ActivationOS + 1.0.0+ + + This is an activator which will detect an operating system's attributes in order to activate + its profile. + + + + name + 1.0.0+ + String + + The name of the OS to be used to activate a profile. + + + + family + 1.0.0+ + String + + The general family of the OS to be used to activate a profile (e.g. 'windows') + + + + arch + 1.0.0+ + String + + The architecture of the OS to be used to activate a profile. + + + + version + 1.0.0+ + String + + The version of the OS to be used to activate a profile. + + + + + + ActivationFile + 1.0.0+ + + This is the file specification used to activate a profile. The missing value will be the location + of a file that needs to exist, and if it doesn't the profile must run. On the other hand exists will test + for the existence of the file and if it is there will run the profile. + + + + missing + 1.0.0+ + String + + The name of the file that should be missing to activate a profile. Please note, that missing and exists + fields cannot be used together. Only one of them should be used at any one time. + + + + exists + 1.0.0+ + String + + The name of the file that should exist to activate a profile. Please note, that missing and exists + fields cannot be used together. Only one of them should be used at any one time. + + + + + + + InputLocation + 2.0.0+ + + + + + + 2.0.0+ + + + @Override + public String toString() { + return getLineNumber() + " : " + getColumnNumber() + ", " + getSource(); + } + + + + + + InputSource + 2.0.0+ + + + location + 2.0.0+ + String + + The path/URL of the settings definition or {@code null} if unknown. + + + + + + 2.0.0+ + + @Override + public String toString() { + return getLocation(); + } + + + + +
    +
    diff --git a/api/maven-api-settings/src/site/apt/index.apt b/api/maven-api-settings/src/site/apt/index.apt new file mode 100644 index 000000000000..ca71c0d7f736 --- /dev/null +++ b/api/maven-api-settings/src/site/apt/index.apt @@ -0,0 +1,35 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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 +~~ +~~ http://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. + + ----- + Introduction + ----- + Vincent Siveton + ----- + 2006-11-04 + ----- + +Maven 4 API - Immutable Settings Model + + This is strictly the immutable model for Maven settings in <<>> package. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with <<>> inner classes for immutable instances creation. + + See also corresponding {{{../../maven-settings/index.html}Maven classical settings model documentation}}. + \ No newline at end of file diff --git a/api/maven-api-settings/src/site/site.xml b/api/maven-api-settings/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-settings/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/maven-api-settings/src/test/java/org/apache/maven/api/settings/SettingsTest.java b/api/maven-api-settings/src/test/java/org/apache/maven/api/settings/SettingsTest.java new file mode 100644 index 000000000000..b78630752ad6 --- /dev/null +++ b/api/maven-api-settings/src/test/java/org/apache/maven/api/settings/SettingsTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.settings; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class SettingsTest { + + @Test + void testSetLocalRepository() { + Settings s = Settings.newInstance(); + + s = s.withLocalRepository("xxx"); + assertEquals("xxx", s.getLocalRepository()); + + s = s.withLocalRepository("yyy"); + assertEquals("yyy", s.getLocalRepository()); + + s = s.withLocalRepository(null); + assertNull(s.getLocalRepository()); + } +} diff --git a/api/maven-api-spi/pom.xml b/api/maven-api-spi/pom.xml new file mode 100644 index 000000000000..19c06b04a2f5 --- /dev/null +++ b/api/maven-api-spi/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-spi + Maven 4 API :: SPI + Maven 4 API - Maven SPI. + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-model + + + org.apache.maven + maven-api-core + + + org.apache.maven + maven-api-di + + + + diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ExtensibleEnumProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ExtensibleEnumProvider.java new file mode 100644 index 000000000000..46cb8881c817 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ExtensibleEnumProvider.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import java.util.Collection; + +import org.apache.maven.api.ExtensibleEnum; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * An SPI interface to extend Maven with new enum values for extensible enumerations. + *

    + * Maven uses extensible enumerations to allow plugins and extensions to add new values + * to various categories like languages, scopes, and packaging types. This interface is the + * base for all providers that register such extensions. + *

    + * Implementations of this interface are discovered through the Java ServiceLoader mechanism. + * Each implementation must be registered in a {@code META-INF/services/} file corresponding + * to the specific provider interface being implemented. + *

    + * Example implementation for a custom language provider: + *

    + * public class CustomLanguageProvider implements LanguageProvider {
    + *     public Collection<Language> provides() {
    + *         return Arrays.asList(
    + *             language("kotlin"),
    + *             language("scala")
    + *         );
    + *     }
    + * }
    + * 
    + * + * @param The type of extensible enum to extend + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface ExtensibleEnumProvider extends SpiService { + + /** + * Provides new values for the extensible enum. + *

    + * This method is called by Maven during initialization to collect all custom enum values + * that should be registered. The returned collection should contain all the enum values + * that this provider wants to contribute. + *

    + * The values returned by this method should be created using the appropriate factory methods + * for the specific enum type, such as {@code language()}, {@code projectScope()}, or + * {@code pathScope()}. + * + * @return a non-null collection of enum instances to register + */ + @Nonnull + Collection provides(); +} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LanguageProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LanguageProvider.java new file mode 100644 index 000000000000..7a2f77068fe9 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LanguageProvider.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.Language; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Service provider interface for registering custom {@link Language} implementations. + *

    + * This interface allows plugins and extensions to define and register additional programming languages + * beyond the standard ones provided by Maven. Implementations of this interface will be discovered + * through the Java ServiceLoader mechanism and their provided languages will be available + * throughout the Maven build process. + *

    + * Example usage: + *

    + * public class CustomLanguageProvider implements LanguageProvider {
    + *     public Collection<Language> provides() {
    + *         return Collections.singleton(language("kotlin"));
    + *     }
    + * }
    + * 
    + * + * @see org.apache.maven.api.Language + * @see org.apache.maven.api.spi.ExtensibleEnumProvider + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface LanguageProvider extends ExtensibleEnumProvider {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LifecycleProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LifecycleProvider.java new file mode 100644 index 000000000000..d37a327e5288 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LifecycleProvider.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.Lifecycle; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Service provider interface for registering custom {@link Lifecycle} implementations. + *

    + * This interface allows plugins and extensions to define and register additional build lifecycles + * beyond the standard ones provided by Maven (like clean, default, site). Lifecycles define a sequence + * of phases that can be executed during a build. + *

    + * Implementations of this interface will be discovered through the Java ServiceLoader mechanism + * and their provided lifecycles will be available throughout the Maven build process. + *

    + * Example usage: + *

    + * public class CustomLifecycleProvider implements LifecycleProvider {
    + *     public Collection<Lifecycle> provides() {
    + *         return Collections.singleton(
    + *             lifecycle("deploy-docker", Arrays.asList(
    + *                 "build-image",
    + *                 "tag-image",
    + *                 "push-image"
    + *             ))
    + *         );
    + *     }
    + * }
    + * 
    + * + * @see org.apache.maven.api.Lifecycle + * @see org.apache.maven.api.spi.ExtensibleEnumProvider + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface LifecycleProvider extends ExtensibleEnumProvider {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java new file mode 100644 index 000000000000..9447e519f57e --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.services.Source; + +/** + * The {@code ModelParser} interface is used to locate and read {@link Model}s from the file system. + * This allows plugging in additional syntaxes for the main model read by Maven when building a project. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface ModelParser extends SpiService { + + /** + * Option that can be specified in the options map. The value should be a Boolean. + */ + String STRICT = "strict"; + + /** + * Locates the pom in the given directory. + * + * @param dir the directory to locate the pom for, never {@code null} + * @return a {@code Source} pointing to the located pom or an empty {@code Optional} if none was found by this parser + */ + @Nonnull + Optional locate(@Nonnull Path dir); + + /** + * Parse the model obtained previously by a previous call to {@link #locate(Path)}. + * + * @param source the source to parse, never {@code null} + * @param options possible parsing options, may be {@code null} + * @return the parsed {@link Model}, never {@code null} + * @throws ModelParserException if the model cannot be parsed + */ + @Nonnull + Model parse(@Nonnull Source source, @Nullable Map options) throws ModelParserException; + + /** + * Locate and parse the model in the specified directory. + * This is equivalent to {@code locate(dir).map(s -> parse(s, options))}. + * + * @param dir the directory to locate the pom for, never {@code null} + * @param options possible parsing options, may be {@code null} + * @return an optional parsed {@link Model} or {@code null} if none could be found + * @throws ModelParserException if the located model cannot be parsed + */ + @Nonnull + default Optional locateAndParse(@Nonnull Path dir, @Nullable Map options) + throws ModelParserException { + return locate(dir).map(s -> parse(s, options)); + } +} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java new file mode 100644 index 000000000000..4520f0650b3c --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.MavenException; + +@Experimental +public class ModelParserException extends MavenException { + + /** + * The one-based index of the line containing the error. + */ + private final int lineNumber; + + /** + * The one-based index of the column containing the error. + */ + private final int columnNumber; + + public ModelParserException() { + this(null, null); + } + + public ModelParserException(String message) { + this(message, null); + } + + public ModelParserException(String message, Throwable cause) { + this(message, -1, -1, cause); + } + + public ModelParserException(String message, int lineNumber, int columnNumber, Throwable cause) { + super(message, cause); + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + public ModelParserException(Throwable cause) { + this(null, cause); + } + + public int getLineNumber() { + return lineNumber; + } + + public int getColumnNumber() { + return columnNumber; + } +} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java new file mode 100644 index 000000000000..50cc19396900 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.model.Model; + +/** + * Interface for model transformers that can modify Maven project models at different stages of processing. + *

    + * Model transformers allow plugins and extensions to modify the POM model during the build process. + * Transformations can be applied at three different stages: + *

      + *
    1. File model - The raw model as read directly from the file
    2. + *
    3. Raw model - The model after inheritance has been applied
    4. + *
    5. Effective model - The fully processed model with all interpolation and inheritance applied
    6. + *
    + *

    + * Implementations of this interface will be discovered through the Java ServiceLoader mechanism + * and will be called in sequence during model building. + *

    + * Example usage: + *

    + * public class CustomModelTransformer implements ModelTransformer {
    + *     public Model transformEffectiveModel(Model model) throws ModelTransformerException {
    + *         // Add a custom property to all models
    + *         model.getProperties().put("custom.timestamp", System.currentTimeMillis());
    + *         return model;
    + *     }
    + * }
    + * 
    + * + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface ModelTransformer extends SpiService { + + /** + * Apply a transformation on the file model. + * + * This method will be called on each file model being loaded, + * just before validation. + * + * @param model the input model + * @return the transformed model, or the input model if no transformation is needed + * @throws ModelTransformerException + */ + @Nonnull + default Model transformFileModel(@Nonnull Model model) throws ModelTransformerException { + return model; + } + + /** + * Apply a transformation on the raw models. + * + * This method will be called on each raw model being loaded, + * just before validation. + * + * @param model the input model + * @return the transformed model, or the input model if no transformation is needed + * @throws ModelTransformerException + */ + @Nonnull + default Model transformRawModel(@Nonnull Model model) throws ModelTransformerException { + return model; + } + + /** + * Apply a transformation on the effective models. + * + * This method will be called on each effective model being loaded, + * just before validation. + * + * @param model the input model + * @return the transformed model, or the input model if no transformation is needed + * @throws ModelTransformerException + */ + @Nonnull + default Model transformEffectiveModel(@Nonnull Model model) throws ModelTransformerException { + return model; + } +} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformerException.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformerException.java new file mode 100644 index 000000000000..d2f844382690 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformerException.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.services.MavenException; + +@Experimental +public class ModelTransformerException extends MavenException { + + public ModelTransformerException() { + this(null, null); + } + + public ModelTransformerException(String message) { + this(message, null); + } + + public ModelTransformerException(Throwable cause) { + this(null, cause); + } + + public ModelTransformerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java new file mode 100644 index 000000000000..2e2af56e8263 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.Packaging; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Service provider interface for registering custom {@link Packaging} implementations. + *

    + * This interface allows plugins and extensions to define and register additional packaging types + * beyond the standard ones provided by Maven (like jar, war, ear, etc.). Implementations of this + * interface will be discovered through the Java ServiceLoader mechanism and their provided + * packaging types will be available throughout the Maven build process. + *

    + * Example usage: + *

    + * public class CustomPackagingProvider implements PackagingProvider {
    + *     public Collection<Packaging> provides() {
    + *         return Arrays.asList(
    + *             packaging("docker-image"),
    + *             packaging("flatpak")
    + *         );
    + *     }
    + * }
    + * 
    + * + * @see org.apache.maven.api.Packaging + * @see org.apache.maven.api.spi.ExtensibleEnumProvider + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface PackagingProvider extends ExtensibleEnumProvider {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PathScopeProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PathScopeProvider.java new file mode 100644 index 000000000000..7d618ae1108a --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PathScopeProvider.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.PathScope; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Service provider interface for registering custom {@link PathScope} implementations. + *

    + * This interface allows plugins and extensions to define and register additional path scopes + * beyond the standard ones provided by Maven. Path scopes define how dependencies are used + * in different contexts, such as compilation, testing, or runtime. + *

    + * Implementations of this interface will be discovered through the Java ServiceLoader mechanism + * and their provided path scopes will be available throughout the Maven build process. + *

    + * Example usage: + *

    + * public class CustomPathScopeProvider implements PathScopeProvider {
    + *     public Collection<PathScope> provides() {
    + *         return Collections.singleton(pathScope("integration-test",
    + *                 ProjectScope.TEST, DependencyScope.TEST));
    + *     }
    + * }
    + * 
    + * + * @see org.apache.maven.api.PathScope + * @see org.apache.maven.api.spi.ExtensibleEnumProvider + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface PathScopeProvider extends ExtensibleEnumProvider {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ProjectScopeProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ProjectScopeProvider.java new file mode 100644 index 000000000000..bcd071e9be48 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ProjectScopeProvider.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.ProjectScope; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Service provider interface for registering custom {@link ProjectScope} implementations. + *

    + * This interface allows plugins and extensions to define and register additional project scopes + * beyond the standard {@link ProjectScope#MAIN} and {@link ProjectScope#TEST} scopes. + * Implementations of this interface will be discovered through the Java ServiceLoader mechanism + * and their provided project scopes will be available throughout the Maven build process. + *

    + * Example usage: + *

    + * public class CustomProjectScopeProvider implements ProjectScopeProvider {
    + *     public Collection<ProjectScope> provides() {
    + *         return Collections.singleton(projectScope("integration-test"));
    + *     }
    + * }
    + * 
    + * + * @see org.apache.maven.api.ProjectScope + * @see org.apache.maven.api.spi.ExtensibleEnumProvider + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface ProjectScopeProvider extends ExtensibleEnumProvider {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PropertyContributor.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PropertyContributor.java new file mode 100644 index 000000000000..97bd4f24300c --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PropertyContributor.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Component able to contribute to Maven session user properties. This SPI component is invoked + * very early, while there is no session created yet. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface PropertyContributor extends SpiService { + /** + * Invoked just before session is created with a mutable map that carries collected user properties so far. + * + * @param userProperties The mutable user properties, never {@code null}. + * @see #contribute(ProtoSession) + */ + default void contribute(Map userProperties) {} + + /** + * Invoked just before session is created with proto session instance. The proto session contains user and + * system properties collected so far, along with other information. This method should return altered + * (contributions applied) user properties, not only the "new" or "added" properties! + * + * @param protoSession The proto session, never {@code null}. + * @return The user properties with contributions. + */ + default Map contribute(ProtoSession protoSession) { + HashMap userProperties = new HashMap<>(protoSession.getUserProperties()); + contribute(userProperties); + return userProperties; + } +} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/SpiService.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/SpiService.java new file mode 100644 index 000000000000..896cd8a29a68 --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/SpiService.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; + +/** + * Marker interface to indicate services that can be provided by plugins and extensions. + *

    + * This interface serves as the base for all Service Provider Interface (SPI) components in Maven. + * Classes implementing this interface can be discovered and loaded by Maven through the + * Java ServiceLoader mechanism, allowing plugins and extensions to contribute functionality + * to the Maven build process. + *

    + * SPI services are typically registered in {@code META-INF/services/} files corresponding to + * the specific service interface being implemented. + *

    + * All SPI services should be annotated with {@link Consumer} to indicate they are meant to be + * implemented by plugins and extensions rather than used by them. + * + * @since 4.0.0 + */ +@Experimental +@Consumer +public interface SpiService {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/TypeProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/TypeProvider.java new file mode 100644 index 000000000000..1aa5bf78d81b --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/TypeProvider.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.spi; + +import org.apache.maven.api.Type; +import org.apache.maven.api.annotations.Consumer; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.di.Named; + +/** + * Service provider interface for registering custom {@link Type} implementations. + *

    + * This interface allows plugins and extensions to define and register additional artifact types + * beyond the standard ones provided by Maven (like jar, war, pom, etc.). Types define how artifacts + * are handled, including their default extension, classifier, and language. + *

    + * Implementations of this interface will be discovered through the Java ServiceLoader mechanism + * and their provided types will be available throughout the Maven build process. + *

    + * Example usage: + *

    + * public class CustomTypeProvider implements TypeProvider {
    + *     public Collection<Type> provides() {
    + *         return Arrays.asList(
    + *             type("kotlin-library", "jar", "kotlin"),
    + *             type("docker-image", "tar.gz", null)
    + *         );
    + *     }
    + * }
    + * 
    + * + * @see org.apache.maven.api.Type + * @see org.apache.maven.api.spi.ExtensibleEnumProvider + * @since 4.0.0 + */ +@Experimental +@Consumer +@Named +public interface TypeProvider extends ExtensibleEnumProvider {} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/package-info.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/package-info.java new file mode 100644 index 000000000000..6a4748fa010a --- /dev/null +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Defines the Service Provider Interface (SPI) for Maven extensions, allowing + * third-party implementations to extend and customize Maven's core functionality + * through a stable, versioned API. + * + * @since 4.0.0 + */ +package org.apache.maven.api.spi; diff --git a/api/maven-api-spi/src/site/site.xml b/api/maven-api-spi/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-spi/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/maven-api-toolchain/pom.xml b/api/maven-api-toolchain/pom.xml new file mode 100644 index 000000000000..64d53566d3ec --- /dev/null +++ b/api/maven-api-toolchain/pom.xml @@ -0,0 +1,108 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-toolchain + + Maven 4 API :: Toolchain + Maven 4 API - Immutable Toolchain model. + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-xml + + + + + + + org.codehaus.modello + modello-maven-plugin + + 1.2.0 + ${project.basedir}/../../src/mdo + + src/main/mdo/toolchains.mdo + + + + + + packageModelV4=org.apache.maven.api.toolchain + locationTracking=true + generateLocationClasses=true + + + + + modello + + velocity + xdoc + xsd + + generate-resources + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/package-info.java + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + **/package-info.java + + + **/*.java + + false + true + + + + + + diff --git a/api/maven-api-toolchain/src/main/java/org/apache/maven/api/toolchain/package-info.java b/api/maven-api-toolchain/src/main/java/org/apache/maven/api/toolchain/package-info.java new file mode 100644 index 000000000000..04661e78d20e --- /dev/null +++ b/api/maven-api-toolchain/src/main/java/org/apache/maven/api/toolchain/package-info.java @@ -0,0 +1,9 @@ +// CHECKSTYLE_OFF: RegexpHeader +/** + * Provides classes for managing Maven toolchains, which allow projects to use specific + * tool installations (like JDKs, compilers, or other build tools) across different + * environments without hardcoding paths. + * + * @since 4.0.0 + */ +package org.apache.maven.api.toolchain; diff --git a/api/maven-api-toolchain/src/main/mdo/toolchains.mdo b/api/maven-api-toolchain/src/main/mdo/toolchains.mdo new file mode 100644 index 000000000000..9568c4732bbd --- /dev/null +++ b/api/maven-api-toolchain/src/main/mdo/toolchains.mdo @@ -0,0 +1,254 @@ + + + + + toolchains + MavenToolchains + The default location for the toolchains file is {@code ~/.m2/toolchains.xml} +

    A Toolchain is a preconfigured object that Maven plugins can use for tool configuration retrieval (location and other information). +

    The toolchains-plugin can read available toolchains on the user's computer + and match them against the toolchain requirements of the project (as configured in {@code pom.xml}): + if match is found, the toolchain instance is made available to other Maven plugins.

    +

    With {@code jdk} toolchain, for example, instead of being stuck with the JDK used to run Maven, all plugins can use + the same other JDK instance without hardcoding absolute paths into the {@code pom.xml} + and without configuring every plugin that require path to JDK tools.

    + + @see Guide to Using Toolchains + ]]>
    + + + + package + org.apache.maven.toolchain.model + + + + + + TrackableBase + 1.1.0+ + + Common base class that contains code to track the source for this instance. + + + + 1.1.0/1.1.0 + + + + + + + + PersistedToolchains + TrackableBase + + The {@code <toolchains>} element is the root of the descriptor. + The following table lists all the possible child elements. + + 1.0.0+ + + + toolchains + 1.0.0+ + The toolchain instance definition. + + ToolchainModel + * + + + + + + 1.0.0/1.1.0 + + public void setModelEncoding(String modelEncoding) { + update(getDelegate().with().modelEncoding(modelEncoding).build()); + } + + + + + + ToolchainModel + TrackableBase + 1.0.0+ + Definition of a toolchain instance. + + + type + 1.0.0+ + + + Type of toolchain:<ul> + <li>{@code jdk} for <a + href="https://maven.apache.org/plugins/maven-toolchains-plugin/toolchains/jdk.html">JDK Standard Toolchain</a>,</li> + <li>other value for <a + href="https://maven.apache.org/plugins/maven-toolchains-plugin/toolchains/custom.html">Custom Toolchain</a></li> + </ul> + + String + + + provides + 1.0.0/1.0.99 + DOM + + Toolchain identification information, which will be matched against project requirements. + <p>Actual content structure is completely open: each toolchain type will define its own format and + semantics. + <p>This is generally a properties format: {@code <name>value</name>} with predefined + properties names. + + + + provides + 1.1.0+ + Properties + + String + * + + + + Toolchain identification information, which will be matched against project requirements. + <p>Each toolchain defines its own properties names and semantics. + + + + configuration + 1.0.0+ + DOM + + Toolchain configuration information, like location or any information that is to be retrieved. + <p>Actual content structure is completely open: each toolchain type will define its own format and + semantics. + <p>In general, this is a properties format: {@code <name>value</name>} with per-toolchain + defined properties names. + + + + + + 1.2.0+ + Generated hashCode() and equals() based on identifier also calls its super, which breaks comparison + + + + + + + + + + InputLocation + 1.2.0+ + + + + + + 1.2.0+ + + + @Override + public String toString() { + return getLineNumber() + " : " + getColumnNumber() + ", " + getSource(); + } + + + + + + InputSource + 1.2.0+ + + + location + 1.2.0+ + String + + The path/URL of the settings definition or {@code null} if unknown. + + + + + + 1.2.0+ + + @Override + public String toString() { + return getLocation(); + } + + + + + +
    + diff --git a/api/maven-api-toolchain/src/site/apt/index.apt b/api/maven-api-toolchain/src/site/apt/index.apt new file mode 100644 index 000000000000..689b0443307e --- /dev/null +++ b/api/maven-api-toolchain/src/site/apt/index.apt @@ -0,0 +1,35 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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 +~~ +~~ http://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. + + ----- + Introduction + ----- + Vincent Siveton + ----- + 2006-11-04 + ----- + +Maven 4 API - Immutable Toolchains Model + + This is strictly the immutable model for Maven toolchains in <<>> package. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with <<>> inner classes for immutable instances creation. + + See also corresponding {{{../../maven-toolchain-model/index.html}Maven classical toolchains model documentation}}. + \ No newline at end of file diff --git a/api/maven-api-toolchain/src/site/site.xml b/api/maven-api-toolchain/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-toolchain/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/maven-api-xml/pom.xml b/api/maven-api-xml/pom.xml new file mode 100644 index 000000000000..4842b6541f07 --- /dev/null +++ b/api/maven-api-xml/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-xml + Maven 4 API :: XML + Maven 4 API - Immutable XML. + + + + org.apache.maven + maven-api-annotations + + + + diff --git a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java new file mode 100644 index 000000000000..80abc22030aa --- /dev/null +++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.xml; + +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * This should be removed when https://bugs.openjdk.org/browse/JDK-8323729 + * is released in our minimum JDK. + */ +class ImmutableCollections { + + private static final Map EMPTY_MAP = new AbstractImmutableMap<>() { + @Override + public Set> entrySet() { + return new AbstractImmutableSet<>() { + @Override + public Iterator> iterator() { + return new Iterator<>() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public Entry next() { + throw new NoSuchElementException(); + } + }; + } + + @Override + public int size() { + return 0; + } + }; + } + }; + + static List copy(Collection collection) { + return collection == null ? List.of() : List.copyOf(collection); + } + + static Map copy(Map map) { + if (map == null) { + return emptyMap(); + } else if (map instanceof AbstractImmutableMap) { + return map; + } else { + switch (map.size()) { + case 0: + return emptyMap(); + case 1: + Map.Entry entry = map.entrySet().iterator().next(); + return singletonMap(entry.getKey(), entry.getValue()); + default: + return new MapN<>(map); + } + } + } + + @SuppressWarnings("unchecked") + static Map emptyMap() { + return (Map) EMPTY_MAP; + } + + static Map singletonMap(K key, V value) { + return new Map1<>(key, value); + } + + private static class Map1 extends AbstractImmutableMap { + private final Entry entry; + + private Map1(K key, V value) { + this.entry = new SimpleImmutableEntry<>(key, value); + } + + @Override + public Set> entrySet() { + return new AbstractImmutableSet<>() { + @Override + public Iterator> iterator() { + return new Iterator<>() { + int index = 0; + + @Override + public boolean hasNext() { + return index == 0; + } + + @Override + public Entry next() { + if (index++ == 0) { + return entry; + } + throw new NoSuchElementException(); + } + }; + } + + @Override + public int size() { + return 1; + } + }; + } + } + + private static class MapN extends AbstractImmutableMap { + private final Object[] entries; + + private MapN(Map map) { + if (map != null) { + entries = new Object[map.size()]; + int idx = 0; + for (Entry e : map.entrySet()) { + entries[idx++] = new SimpleImmutableEntry<>(e.getKey(), e.getValue()); + } + } else { + entries = new Object[0]; + } + } + + @Override + public Set> entrySet() { + return new AbstractImmutableSet<>() { + @Override + public Iterator> iterator() { + return new Iterator<>() { + int index = 0; + + @Override + public boolean hasNext() { + return index < entries.length; + } + + @SuppressWarnings("unchecked") + @Override + public Entry next() { + if (index < entries.length) { + return (Entry) entries[index++]; + } + throw new NoSuchElementException(); + } + }; + } + + @Override + public int size() { + return entries.length; + } + }; + } + } + + private abstract static class AbstractImmutableMap extends AbstractMap implements Serializable { + @Override + public void replaceAll(BiFunction function) { + throw uoe(); + } + + @Override + public V putIfAbsent(K key, V value) { + throw uoe(); + } + + @Override + public boolean remove(Object key, Object value) { + throw uoe(); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + throw uoe(); + } + + @Override + public V replace(K key, V value) { + throw uoe(); + } + + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + throw uoe(); + } + + @Override + public V computeIfPresent(K key, BiFunction remappingFunction) { + throw uoe(); + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + throw uoe(); + } + + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + throw uoe(); + } + } + + private abstract static class AbstractImmutableSet extends AbstractSet implements Serializable { + @Override + public boolean removeAll(Collection c) { + throw uoe(); + } + + @Override + public boolean add(E e) { + throw uoe(); + } + + @Override + public boolean remove(Object o) { + throw uoe(); + } + + @Override + public boolean retainAll(Collection c) { + throw uoe(); + } + + @Override + public void clear() { + throw uoe(); + } + + @Override + public boolean removeIf(Predicate filter) { + throw uoe(); + } + } + + private static UnsupportedOperationException uoe() { + return new UnsupportedOperationException(); + } +} diff --git a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java new file mode 100644 index 000000000000..7cf78c9cc199 --- /dev/null +++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java @@ -0,0 +1,549 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.xml; + +import java.io.IOException; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.annotations.ThreadSafe; + +/** + * An immutable XML node representation that provides a clean API for working with XML data structures. + * This interface represents a single node in an XML document tree, containing information about + * the node's name, value, attributes, and child nodes. + * + *

    Example usage:

    + *
    + * XmlNode node = XmlNode.newBuilder()
    + *     .name("configuration")
    + *     .attribute("version", "1.0")
    + *     .child(XmlNode.newInstance("property", "value"))
    + *     .build();
    + * 
    + * + * @since 4.0.0 + */ +@Experimental +@ThreadSafe +@Immutable +public interface XmlNode { + + @Deprecated(since = "4.0.0", forRemoval = true) + String CHILDREN_COMBINATION_MODE_ATTRIBUTE = XmlService.CHILDREN_COMBINATION_MODE_ATTRIBUTE; + + @Deprecated(since = "4.0.0", forRemoval = true) + String CHILDREN_COMBINATION_MERGE = XmlService.CHILDREN_COMBINATION_MERGE; + + @Deprecated(since = "4.0.0", forRemoval = true) + String CHILDREN_COMBINATION_APPEND = XmlService.CHILDREN_COMBINATION_APPEND; + + /** + * This default mode for combining children DOMs during merge means that where element names match, the process will + * try to merge the element data, rather than putting the dominant and recessive elements (which share the same + * element name) as siblings in the resulting DOM. + */ + @Deprecated(since = "4.0.0", forRemoval = true) + String DEFAULT_CHILDREN_COMBINATION_MODE = XmlService.DEFAULT_CHILDREN_COMBINATION_MODE; + + @Deprecated(since = "4.0.0", forRemoval = true) + String SELF_COMBINATION_MODE_ATTRIBUTE = XmlService.SELF_COMBINATION_MODE_ATTRIBUTE; + + @Deprecated(since = "4.0.0", forRemoval = true) + String SELF_COMBINATION_OVERRIDE = XmlService.SELF_COMBINATION_OVERRIDE; + + @Deprecated(since = "4.0.0", forRemoval = true) + String SELF_COMBINATION_MERGE = XmlService.SELF_COMBINATION_MERGE; + + @Deprecated(since = "4.0.0", forRemoval = true) + String SELF_COMBINATION_REMOVE = XmlService.SELF_COMBINATION_REMOVE; + + /** + * In case of complex XML structures, combining can be done based on id. + */ + @Deprecated(since = "4.0.0", forRemoval = true) + String ID_COMBINATION_MODE_ATTRIBUTE = XmlService.ID_COMBINATION_MODE_ATTRIBUTE; + + /** + * In case of complex XML structures, combining can be done based on keys. + * This is a comma separated list of attribute names. + */ + @Deprecated(since = "4.0.0", forRemoval = true) + String KEYS_COMBINATION_MODE_ATTRIBUTE = XmlService.KEYS_COMBINATION_MODE_ATTRIBUTE; + + /** + * This default mode for combining a DOM node during merge means that where element names match, the process will + * try to merge the element attributes and values, rather than overriding the recessive element completely with the + * dominant one. This means that wherever the dominant element doesn't provide the value or a particular attribute, + * that value or attribute will be set from the recessive DOM node. + */ + @Deprecated(since = "4.0.0", forRemoval = true) + String DEFAULT_SELF_COMBINATION_MODE = XmlService.DEFAULT_SELF_COMBINATION_MODE; + + /** + * Returns the local name of this XML node. + * + * @return the node name, never {@code null} + */ + @Nonnull + String name(); + + /** + * Returns the namespace URI of this XML node. + * + * @return the namespace URI, never {@code null} (empty string if no namespace) + */ + @Nonnull + String namespaceUri(); + + /** + * Returns the namespace prefix of this XML node. + * + * @return the namespace prefix, never {@code null} (empty string if no prefix) + */ + @Nonnull + String prefix(); + + /** + * Returns the text content of this XML node. + * + * @return the node's text value, or {@code null} if none exists + */ + @Nullable + String value(); + + /** + * Returns an immutable map of all attributes defined on this XML node. + * + * @return map of attribute names to values, never {@code null} + */ + @Nonnull + Map attributes(); + + /** + * Returns the value of a specific attribute. + * + * @param name the name of the attribute to retrieve + * @return the attribute value, or {@code null} if the attribute doesn't exist + * @throws NullPointerException if name is null + */ + @Nullable + String attribute(@Nonnull String name); + + /** + * Returns an immutable list of all child nodes. + * + * @return list of child nodes, never {@code null} + */ + @Nonnull + List children(); + + /** + * Returns the first child node with the specified name. + * + * @param name the name of the child node to find + * @return the first matching child node, or {@code null} if none found + */ + @Nullable + XmlNode child(String name); + + /** + * Returns the input location information for this node, if available. + * This can be useful for error reporting and debugging. + * + * @return the input location object, or {@code null} if not available + */ + @Nullable + Object inputLocation(); + + // Deprecated methods that delegate to new ones + @Deprecated(since = "4.0.0", forRemoval = true) + @Nonnull + default String getName() { + return name(); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nonnull + default String getNamespaceUri() { + return namespaceUri(); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nonnull + default String getPrefix() { + return prefix(); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nullable + default String getValue() { + return value(); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nonnull + default Map getAttributes() { + return attributes(); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nullable + default String getAttribute(@Nonnull String name) { + return attribute(name); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nonnull + default List getChildren() { + return children(); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nullable + default XmlNode getChild(String name) { + return child(name); + } + + @Deprecated(since = "4.0.0", forRemoval = true) + @Nullable + default Object getInputLocation() { + return inputLocation(); + } + + /** + * @deprecated use {@link XmlService#merge(XmlNode, XmlNode, Boolean)} instead + */ + @Deprecated(since = "4.0.0", forRemoval = true) + default XmlNode merge(@Nullable XmlNode source) { + return XmlService.merge(this, source); + } + + /** + * @deprecated use {@link XmlService#merge(XmlNode, XmlNode, Boolean)} instead + */ + @Deprecated(since = "4.0.0", forRemoval = true) + default XmlNode merge(@Nullable XmlNode source, @Nullable Boolean childMergeOverride) { + return XmlService.merge(this, source, childMergeOverride); + } + + /** + * Merge recessive into dominant and return either {@code dominant} + * with merged information or a clone of {@code recessive} if + * {@code dominant} is {@code null}. + * + * @param dominant the node + * @param recessive if {@code null}, nothing will happen + * @return the merged node + * + * @deprecated use {@link XmlService#merge(XmlNode, XmlNode, Boolean)} instead + */ + @Deprecated(since = "4.0.0", forRemoval = true) + @Nullable + static XmlNode merge(@Nullable XmlNode dominant, @Nullable XmlNode recessive) { + return XmlService.merge(dominant, recessive, null); + } + + @Nullable + static XmlNode merge( + @Nullable XmlNode dominant, @Nullable XmlNode recessive, @Nullable Boolean childMergeOverride) { + return XmlService.merge(dominant, recessive, childMergeOverride); + } + + /** + * Creates a new XmlNode instance with the specified name. + * + * @param name the name for the new node + * @return a new XmlNode instance + * @throws NullPointerException if name is null + */ + static XmlNode newInstance(String name) { + return newBuilder().name(name).build(); + } + + /** + * Creates a new XmlNode instance with the specified name and value. + * + * @param name the name for the new node + * @param value the value for the new node + * @return a new XmlNode instance + * @throws NullPointerException if name is null + */ + static XmlNode newInstance(String name, String value) { + return newBuilder().name(name).value(value).build(); + } + + /** + * Creates a new XmlNode instance with the specified name and children. + * + * @param name the name for the new node + * @param children the list of child nodes + * @return a new XmlNode instance + * @throws NullPointerException if name is null + */ + static XmlNode newInstance(String name, List children) { + return newBuilder().name(name).children(children).build(); + } + + /** + * Creates a new XmlNode instance with all properties specified. + * + * @param name the name for the new node + * @param value the value for the new node + * @param attrs the attributes for the new node + * @param children the list of child nodes + * @param location the input location information + * @return a new XmlNode instance + * @throws NullPointerException if name is null + */ + static XmlNode newInstance( + String name, String value, Map attrs, List children, Object location) { + return newBuilder() + .name(name) + .value(value) + .attributes(attrs) + .children(children) + .inputLocation(location) + .build(); + } + + /** + * Returns a new builder for creating XmlNode instances. + * + * @return a new Builder instance + */ + static Builder newBuilder() { + return new Builder(); + } + + /** + * Builder class for creating XmlNode instances. + *

    + * This builder provides a fluent API for setting the various properties of an XML node. + * All properties are optional except for the node name, which must be set before calling + * {@link #build()}. + */ + class Builder { + private String name; + private String value; + private String namespaceUri; + private String prefix; + private Map attributes; + private List children; + private Object inputLocation; + + /** + * Sets the name of the XML node. + *

    + * This is the only required property that must be set before calling {@link #build()}. + * + * @param name the name of the XML node + * @return this builder instance + * @throws NullPointerException if name is null + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Sets the text content of the XML node. + * + * @param value the text content of the XML node + * @return this builder instance + */ + public Builder value(String value) { + this.value = value; + return this; + } + + /** + * Sets the namespace URI of the XML node. + * + * @param namespaceUri the namespace URI of the XML node + * @return this builder instance + */ + public Builder namespaceUri(String namespaceUri) { + this.namespaceUri = namespaceUri; + return this; + } + + /** + * Sets the namespace prefix of the XML node. + * + * @param prefix the namespace prefix of the XML node + * @return this builder instance + */ + public Builder prefix(String prefix) { + this.prefix = prefix; + return this; + } + + /** + * Sets the attributes of the XML node. + *

    + * The provided map will be copied to ensure immutability. + * + * @param attributes the map of attribute names to values + * @return this builder instance + */ + public Builder attributes(Map attributes) { + this.attributes = attributes; + return this; + } + + /** + * Sets the child nodes of the XML node. + *

    + * The provided list will be copied to ensure immutability. + * + * @param children the list of child nodes + * @return this builder instance + */ + public Builder children(List children) { + this.children = children; + return this; + } + + /** + * Sets the input location information for the XML node. + *

    + * This is typically used for error reporting and debugging purposes. + * + * @param inputLocation the input location object + * @return this builder instance + */ + public Builder inputLocation(Object inputLocation) { + this.inputLocation = inputLocation; + return this; + } + + /** + * Builds a new XmlNode instance with the current builder settings. + * + * @return a new immutable XmlNode instance + * @throws NullPointerException if name has not been set + */ + public XmlNode build() { + return new Impl(prefix, namespaceUri, name, value, attributes, children, inputLocation); + } + + private record Impl( + String prefix, + String namespaceUri, + @Nonnull String name, + String value, + @Nonnull Map attributes, + @Nonnull List children, + Object inputLocation) + implements XmlNode, Serializable { + + private Impl { + // Validation and normalization from the original constructor + prefix = prefix == null ? "" : prefix; + namespaceUri = namespaceUri == null ? "" : namespaceUri; + name = Objects.requireNonNull(name); + attributes = ImmutableCollections.copy(attributes); + children = ImmutableCollections.copy(children); + } + + @Override + public String attribute(@Nonnull String name) { + return attributes.get(name); + } + + @Override + public XmlNode child(String name) { + if (name != null) { + ListIterator it = children.listIterator(children.size()); + while (it.hasPrevious()) { + XmlNode child = it.previous(); + if (name.equals(child.name())) { + return child; + } + } + } + return null; + } + + @Override + public boolean equals(Object o) { + return this == o + || o instanceof XmlNode that + && Objects.equals(this.name, that.name()) + && Objects.equals(this.value, that.value()) + && Objects.equals(this.attributes, that.attributes()) + && Objects.equals(this.children, that.children()); + } + + @Override + public int hashCode() { + return Objects.hash(name, value, attributes, children); + } + + @Override + public String toString() { + try { + StringWriter writer = new StringWriter(); + XmlService.write(this, writer); + return writer.toString(); + } catch (IOException e) { + return toStringObject(); + } + } + + private String toStringObject() { + StringBuilder sb = new StringBuilder(); + sb.append("XmlNode["); + boolean w = false; + w = addToStringField(sb, prefix, o -> !o.isEmpty(), "prefix", w); + w = addToStringField(sb, namespaceUri, o -> !o.isEmpty(), "namespaceUri", w); + w = addToStringField(sb, name, o -> !o.isEmpty(), "name", w); + w = addToStringField(sb, value, o -> !o.isEmpty(), "value", w); + w = addToStringField(sb, attributes, o -> !o.isEmpty(), "attributes", w); + w = addToStringField(sb, children, o -> !o.isEmpty(), "children", w); + w = addToStringField(sb, inputLocation, Objects::nonNull, "inputLocation", w); + sb.append("]"); + return sb.toString(); + } + + private static boolean addToStringField( + StringBuilder sb, T o, Function p, String n, boolean w) { + if (!p.apply(o)) { + if (w) { + sb.append(", "); + } else { + w = true; + } + sb.append(n).append("='").append(o).append('\''); + } + return w; + } + } + } +} diff --git a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java new file mode 100644 index 000000000000..e6735e255f54 --- /dev/null +++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java @@ -0,0 +1,248 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.api.xml; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.ServiceLoader; + +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Comprehensive service interface for XML operations including node creation, + * merging, reading, and writing. + * + *

    This class provides XML merging functionality for Maven's XML handling + * and specifies the combination modes that control how XML elements are merged.

    + * + *

    The merger supports two main types of combinations:

    + *
      + *
    • Children combination: Controls how child elements are combined
    • + *
    • Self combination: Controls how the element itself is combined
    • + *
    + * + *

    Children combination modes (specified by {@code combine.children} attribute):

    + *
      + *
    • {@code merge} (default): Merges elements with matching names
    • + *
    • {@code append}: Adds elements as siblings
    • + *
    + * + *

    Self combination modes (specified by {@code combine.self} attribute):

    + *
      + *
    • {@code merge} (default): Merges attributes and values
    • + *
    • {@code override}: Completely replaces the element
    • + *
    • {@code remove}: Removes the element
    • + *
    + * + *

    For complex XML structures, combining can also be done based on:

    + *
      + *
    • ID: Using the {@code combine.id} attribute
    • + *
    • Keys: Using the {@code combine.keys} attribute with comma-separated key names
    • + *
    + * + * @since 4.0.0 + */ +public abstract class XmlService { + + /** Attribute name controlling how child elements are combined */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + /** Value indicating children should be merged based on element names */ + public static final String CHILDREN_COMBINATION_MERGE = "merge"; + /** Value indicating children should be appended as siblings */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + /** + * Default mode for combining children DOMs during merge. + * When element names match, the process will try to merge the element data, + * rather than putting the dominant and recessive elements as siblings. + */ + public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; + + /** Attribute name controlling how the element itself is combined */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + /** Value indicating the element should be completely overridden */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; + /** Value indicating the element should be merged */ + public static final String SELF_COMBINATION_MERGE = "merge"; + /** Value indicating the element should be removed */ + public static final String SELF_COMBINATION_REMOVE = "remove"; + /** + * Default mode for combining a DOM node during merge. + * When element names match, the process will try to merge element attributes + * and values, rather than overriding the recessive element completely. + */ + public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; + + /** Attribute name for ID-based combination mode */ + public static final String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id"; + /** + * Attribute name for key-based combination mode. + * Value should be a comma-separated list of attribute names. + */ + public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = "combine.keys"; + + /** + * Convenience method to merge two XML nodes using default settings. + */ + @Nullable + public static XmlNode merge(XmlNode dominant, XmlNode recessive) { + return merge(dominant, recessive, null); + } + + /** + * Merges two XML nodes. + */ + @Nullable + public static XmlNode merge( + @Nullable XmlNode dominant, @Nullable XmlNode recessive, @Nullable Boolean childMergeOverride) { + return getService().doMerge(dominant, recessive, childMergeOverride); + } + + /** + * Reads an XML node from an input stream. + */ + @Nonnull + public static XmlNode read(InputStream input, @Nullable InputLocationBuilder locationBuilder) + throws XMLStreamException { + return getService().doRead(input, locationBuilder); + } + + /** + * Reads an XML node from a reader. + */ + @Nonnull + public static XmlNode read(Reader reader) throws XMLStreamException { + return read(reader, null); + } + + /** + * Reads an XML node from a reader. + */ + @Nonnull + public static XmlNode read(Reader reader, @Nullable InputLocationBuilder locationBuilder) + throws XMLStreamException { + return getService().doRead(reader, locationBuilder); + } + + /** + * Reads an XML node from an XMLStreamReader. + */ + @Nonnull + public static XmlNode read(XMLStreamReader reader) throws XMLStreamException { + return read(reader, null); + } + + /** + * Reads an XML node from an XMLStreamReader. + */ + @Nonnull + public static XmlNode read(XMLStreamReader reader, @Nullable InputLocationBuilder locationBuilder) + throws XMLStreamException { + return getService().doRead(reader, locationBuilder); + } + + /** + * Writes an XML node to a writer. + */ + public static void write(XmlNode node, Writer writer) throws IOException { + getService().doWrite(node, writer); + } + + /** + * Interface for building input locations during XML parsing. + */ + public interface InputLocationBuilder { + Object toInputLocation(XMLStreamReader parser); + } + + /** + * Implementation method for reading an XML node from an input stream. + * + * @param input the input stream to read from + * @param locationBuilder optional builder for creating input location objects + * @return the parsed XML node + * @throws XMLStreamException if there is an error parsing the XML + */ + protected abstract XmlNode doRead(InputStream input, InputLocationBuilder locationBuilder) + throws XMLStreamException; + + /** + * Implementation method for reading an XML node from a reader. + * + * @param reader the reader to read from + * @param locationBuilder optional builder for creating input location objects + * @return the parsed XML node + * @throws XMLStreamException if there is an error parsing the XML + */ + protected abstract XmlNode doRead(Reader reader, InputLocationBuilder locationBuilder) throws XMLStreamException; + + /** + * Implementation method for reading an XML node from an XMLStreamReader. + * + * @param reader the XML stream reader to read from + * @param locationBuilder optional builder for creating input location objects + * @return the parsed XML node + * @throws XMLStreamException if there is an error parsing the XML + */ + protected abstract XmlNode doRead(XMLStreamReader reader, InputLocationBuilder locationBuilder) + throws XMLStreamException; + + /** + * Implementation method for writing an XML node to a writer. + * + * @param node the XML node to write + * @param writer the writer to write to + * @throws IOException if there is an error writing the XML + */ + protected abstract void doWrite(XmlNode node, Writer writer) throws IOException; + + /** + * Implementation method for merging two XML nodes. + * + * @param dominant the dominant (higher priority) XML node + * @param recessive the recessive (lower priority) XML node + * @param childMergeOverride optional override for the child merge mode + * @return the merged XML node, or null if both inputs are null + */ + protected abstract XmlNode doMerge(XmlNode dominant, XmlNode recessive, Boolean childMergeOverride); + + /** + * Gets the singleton instance of the XmlService. + * + * @return the XmlService instance + * @throws IllegalStateException if no implementation is found + */ + private static XmlService getService() { + return Holder.INSTANCE; + } + + /** Holder class for lazy initialization of the default instance */ + private static final class Holder { + static final XmlService INSTANCE = ServiceLoader.load(XmlService.class) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No XmlService implementation found")); + + private Holder() {} + } +} diff --git a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/package-info.java b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/package-info.java new file mode 100644 index 000000000000..11ecfc229838 --- /dev/null +++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Provides an immutable XML processing API for Maven, offering classes and interfaces + * for reading, manipulating, and writing XML documents in a thread-safe manner. + * This package is used extensively for POM, settings, and other Maven configuration files. + * + * @since 4.0.0 + */ +package org.apache.maven.api.xml; diff --git a/api/maven-api-xml/src/site/site.xml b/api/maven-api-xml/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/maven-api-xml/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 000000000000..b90c9f605c81 --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,97 @@ + + + + 4.0.0 + + + org.apache.maven + maven + 4.1.0-SNAPSHOT + + + maven-api + pom + Maven 4 API + A new immutable API for Maven 4 to better manage what plugins and extensions can influence. + + + maven-api-annotations + maven-api-di + maven-api-xml + maven-api-model + maven-api-plugin + maven-api-settings + maven-api-toolchain + maven-api-metadata + maven-api-core + maven-api-spi + maven-api-cli + + + + api + + + + + reporting + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + provisional + tf + Provisional: + + + + + + aggregate + + aggregate + + false + + + + + org.apache.maven.plugins + maven-jxr-plugin + + + aggregate + + aggregate + + false + + + + + + + + + diff --git a/api/src/site/site.xml b/api/src/site/site.xml new file mode 100644 index 000000000000..6a599fce2b80 --- /dev/null +++ b/api/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + + + + diff --git a/compat/maven-artifact/pom.xml b/compat/maven-artifact/pom.xml new file mode 100644 index 000000000000..42d5bf80f56d --- /dev/null +++ b/compat/maven-artifact/pom.xml @@ -0,0 +1,73 @@ + + + + 4.0.0 + + + org.apache.maven + maven-compat-modules + 4.1.0-SNAPSHOT + + + maven-artifact + + Maven Artifact + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.maven.artifact.versioning.ComparableVersion + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java new file mode 100644 index 000000000000..bbbdc14d8278 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * Maven Artifact interface. Notice that it mixes artifact definition concepts (groupId, artifactId, version) + * with dependency information (version range, scope). + */ +public interface Artifact extends Comparable { + + @Deprecated(since = "4.0.0") + String RELEASE_VERSION = "RELEASE"; + + @Deprecated(since = "4.0.0") + String LATEST_VERSION = "LATEST"; + + String SNAPSHOT_VERSION = "SNAPSHOT"; + + Pattern VERSION_FILE_PATTERN = Pattern.compile("^(.*)-(\\d{8}\\.\\d{6})-(\\d+)$"); + + // TODO into artifactScope handler + + String SCOPE_COMPILE = "compile"; + + String SCOPE_COMPILE_PLUS_RUNTIME = "compile+runtime"; + + String SCOPE_TEST = "test"; + + String SCOPE_RUNTIME = "runtime"; + + String SCOPE_RUNTIME_PLUS_SYSTEM = "runtime+system"; + + String SCOPE_PROVIDED = "provided"; + + String SCOPE_SYSTEM = "system"; + + String SCOPE_IMPORT = "import"; // Used to import dependencyManagement dependencies + + String getGroupId(); + + String getArtifactId(); + + String getVersion(); + + void setVersion(String version); + + String getScope(); + + String getType(); + + String getClassifier(); + + boolean hasClassifier(); + + File getFile(); + + void setFile(File destination); + + String getBaseVersion(); + + void setBaseVersion(String baseVersion); + + String getId(); + + String getDependencyConflictId(); + + void addMetadata(ArtifactMetadata metadata); + + Collection getMetadataList(); + + void setRepository(ArtifactRepository remoteRepository); + + ArtifactRepository getRepository(); + + void updateVersion(String version, ArtifactRepository localRepository); + + String getDownloadUrl(); + + void setDownloadUrl(String downloadUrl); + + ArtifactFilter getDependencyFilter(); + + void setDependencyFilter(ArtifactFilter artifactFilter); + + ArtifactHandler getArtifactHandler(); + + List getDependencyTrail(); + + void setDependencyTrail(List dependencyTrail); + + void setScope(String scope); + + VersionRange getVersionRange(); + + void setVersionRange(VersionRange newRange); + + void selectVersion(String version); + + void setGroupId(String groupId); + + void setArtifactId(String artifactId); + + boolean isSnapshot(); + + void setResolved(boolean resolved); + + boolean isResolved(); + + void setResolvedVersion(String version); + + void setArtifactHandler(ArtifactHandler handler); + + boolean isRelease(); + + void setRelease(boolean release); + + List getAvailableVersions(); + + void setAvailableVersions(List versions); + + boolean isOptional(); + + void setOptional(boolean optional); + + ArtifactVersion getSelectedVersion() throws OverConstrainedVersionException; + + boolean isSelectedVersionKnown() throws OverConstrainedVersionException; +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/ArtifactUtils.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/ArtifactUtils.java new file mode 100644 index 000000000000..8ea6710a56cb --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/ArtifactUtils.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; + +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * ArtifactUtils + */ +public final class ArtifactUtils { + + public static boolean isSnapshot(String version) { + if (version != null) { + if (version.regionMatches( + true, + version.length() - Artifact.SNAPSHOT_VERSION.length(), + Artifact.SNAPSHOT_VERSION, + 0, + Artifact.SNAPSHOT_VERSION.length())) { + return true; + } else { + return Artifact.VERSION_FILE_PATTERN.matcher(version).matches(); + } + } + return false; + } + + public static String toSnapshotVersion(String version) { + notBlank(version, "version can neither be null, empty nor blank"); + + int lastHyphen = version.lastIndexOf('-'); + if (lastHyphen > 0) { + int prevHyphen = version.lastIndexOf('-', lastHyphen - 1); + if (prevHyphen > 0) { + Matcher m = Artifact.VERSION_FILE_PATTERN.matcher(version); + if (m.matches()) { + return m.group(1) + "-" + Artifact.SNAPSHOT_VERSION; + } + } + } + return version; + } + + public static String versionlessKey(Artifact artifact) { + return versionlessKey(artifact.getGroupId(), artifact.getArtifactId()); + } + + public static String versionlessKey(String groupId, String artifactId) { + notBlank(groupId, "groupId can neither be null, empty nor blank"); + notBlank(artifactId, "artifactId can neither be null, empty nor blank"); + + return groupId + ":" + artifactId; + } + + public static String key(Artifact artifact) { + return key(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); + } + + public static String key(String groupId, String artifactId, String version) { + notBlank(groupId, "groupId can neither be null, empty nor blank"); + notBlank(artifactId, "artifactId can neither be null, empty nor blank"); + notBlank(version, "version can neither be null, empty nor blank"); + + return groupId + ":" + artifactId + ":" + version; + } + + private static void notBlank(String str, String message) { + final int strLen = str != null ? str.length() : 0; + if (strLen > 0) { + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return; + } + } + } + throw new IllegalArgumentException(message); + } + + public static Map artifactMapByVersionlessId(Collection artifacts) { + Map artifactMap = new LinkedHashMap<>(); + + if (artifacts != null) { + for (Artifact artifact : artifacts) { + artifactMap.put(versionlessKey(artifact), artifact); + } + } + + return artifactMap; + } + + public static Artifact copyArtifactSafe(Artifact artifact) { + return (artifact != null) ? copyArtifact(artifact) : null; + } + + public static Artifact copyArtifact(Artifact artifact) { + VersionRange range = artifact.getVersionRange(); + + // For some reason with the introduction of MNG-1577 we have the case in Yoko where a depMan section has + // something like the following: + // + // + // + // + // + // org.apache.yoko + // yoko-core + // ${version} + // + // ... + // + // And the range is not set so we'll check here and set it. jvz. + + if (range == null) { + range = VersionRange.createFromVersion(artifact.getVersion()); + } + + DefaultArtifact clone = new DefaultArtifact( + artifact.getGroupId(), + artifact.getArtifactId(), + range, + artifact.getScope(), + artifact.getType(), + artifact.getClassifier(), + artifact.getArtifactHandler(), + artifact.isOptional()); + clone.setRelease(artifact.isRelease()); + clone.setResolvedVersion(artifact.getVersion()); + clone.setResolved(artifact.isResolved()); + clone.setFile(artifact.getFile()); + + clone.setAvailableVersions(copyList(artifact.getAvailableVersions())); + if (artifact.getVersion() != null) { + clone.setBaseVersion(artifact.getBaseVersion()); + } + clone.setDependencyFilter(artifact.getDependencyFilter()); + clone.setDependencyTrail(copyList(artifact.getDependencyTrail())); + clone.setDownloadUrl(artifact.getDownloadUrl()); + clone.setRepository(artifact.getRepository()); + + return clone; + } + + /** + * Copy artifact to a collection. + * + * @param the target collection type + * @param from an artifact collection + * @param to the target artifact collection + * @return to collection + */ + public static > T copyArtifacts(Collection from, T to) { + for (Artifact artifact : from) { + to.add(ArtifactUtils.copyArtifact(artifact)); + } + return to; + } + + public static > T copyArtifacts(Map from, T to) { + if (from != null) { + for (Map.Entry entry : from.entrySet()) { + to.put(entry.getKey(), ArtifactUtils.copyArtifact(entry.getValue())); + } + } + + return to; + } + + private static List copyList(List original) { + List copy = null; + + if (original != null) { + copy = new ArrayList<>(); + + if (!original.isEmpty()) { + copy.addAll(original); + } + } + + return copy; + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java new file mode 100644 index 000000000000..2dc3cf5ac061 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java @@ -0,0 +1,554 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + */ +public class DefaultArtifact implements Artifact { + private String groupId; + + private String artifactId; + + private String baseVersion; + + private final String type; + + private final String classifier; + + private volatile String scope; + + private volatile File file; + + private ArtifactRepository repository; + + private String downloadUrl; + + private ArtifactFilter dependencyFilter; + + private ArtifactHandler artifactHandler; + + private List dependencyTrail; + + private volatile String version; + + private VersionRange versionRange; + + private volatile boolean resolved; + + private boolean release; + + private List availableVersions; + + private Map metadataMap; + + private boolean optional; + + public DefaultArtifact( + String groupId, + String artifactId, + String version, + String scope, + String type, + String classifier, + ArtifactHandler artifactHandler) { + this( + groupId, + artifactId, + VersionRange.createFromVersion(version), + scope, + type, + classifier, + artifactHandler, + false); + } + + @SuppressWarnings("checkstyle:ParameterNumber") + public DefaultArtifact( + String groupId, + String artifactId, + String version, + String scope, + String type, + String classifier, + ArtifactHandler artifactHandler, + boolean optional) { + this( + groupId, + artifactId, + VersionRange.createFromVersion(version), + scope, + type, + classifier, + artifactHandler, + optional); + } + + public DefaultArtifact( + String groupId, + String artifactId, + VersionRange versionRange, + String scope, + String type, + String classifier, + ArtifactHandler artifactHandler) { + this(groupId, artifactId, versionRange, scope, type, classifier, artifactHandler, false); + } + + @SuppressWarnings("checkstyle:parameternumber") + public DefaultArtifact( + String groupId, + String artifactId, + VersionRange versionRange, + String scope, + String type, + String classifier, + ArtifactHandler artifactHandler, + boolean optional) { + this.groupId = groupId; + + this.artifactId = artifactId; + + this.versionRange = versionRange; + + selectVersionFromNewRangeIfAvailable(); + + this.artifactHandler = artifactHandler; + + this.scope = scope; + + this.type = type; + + if (classifier == null) { + classifier = artifactHandler.getClassifier(); + } + + this.classifier = classifier; + + this.optional = optional; + + validateIdentity(); + } + + private void validateIdentity() { + if (empty(groupId)) { + throw new InvalidArtifactRTException( + groupId, artifactId, getVersion(), type, "The groupId cannot be empty."); + } + + if (empty(artifactId)) { + throw new InvalidArtifactRTException( + groupId, artifactId, getVersion(), type, "The artifactId cannot be empty."); + } + + if (empty(type)) { + throw new InvalidArtifactRTException(groupId, artifactId, getVersion(), type, "The type cannot be empty."); + } + + if ((empty(version)) && (versionRange == null)) { + throw new InvalidArtifactRTException( + groupId, artifactId, getVersion(), type, "The version cannot be empty."); + } + } + + public static boolean empty(String value) { + return value == null || value.isBlank(); + } + + @Override + public String getClassifier() { + return classifier; + } + + @Override + public boolean hasClassifier() { + return classifier != null && !classifier.isEmpty(); + } + + @Override + public String getScope() { + return scope; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public String getArtifactId() { + return artifactId; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public void setVersion(String version) { + this.version = version; + setBaseVersionInternal(version); + versionRange = null; + } + + @Override + public String getType() { + return type; + } + + @Override + public void setFile(File file) { + this.file = file; + } + + @Override + public File getFile() { + return file; + } + + @Override + public ArtifactRepository getRepository() { + return repository; + } + + @Override + public void setRepository(ArtifactRepository repository) { + this.repository = repository; + } + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + @Override + public String getId() { + return getDependencyConflictId() + ":" + getBaseVersion(); + } + + @Override + public String getDependencyConflictId() { + StringBuilder sb = new StringBuilder(128); + sb.append(getGroupId()); + sb.append(':'); + appendArtifactTypeClassifierString(sb); + return sb.toString(); + } + + private void appendArtifactTypeClassifierString(StringBuilder sb) { + sb.append(getArtifactId()); + sb.append(':'); + sb.append(getType()); + if (hasClassifier()) { + sb.append(':'); + sb.append(getClassifier()); + } + } + + @Override + public void addMetadata(ArtifactMetadata metadata) { + if (metadataMap == null) { + metadataMap = new HashMap<>(); + } + + ArtifactMetadata m = metadataMap.get(metadata.getKey()); + if (m != null) { + m.merge(metadata); + } else { + metadataMap.put(metadata.getKey(), metadata); + } + } + + @Override + public Collection getMetadataList() { + if (metadataMap == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableCollection(metadataMap.values()); + } + + // ---------------------------------------------------------------------- + // Object overrides + // ---------------------------------------------------------------------- + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (getGroupId() != null) { + sb.append(getGroupId()); + sb.append(':'); + } + appendArtifactTypeClassifierString(sb); + sb.append(':'); + if (getBaseVersionInternal() != null) { + sb.append(getBaseVersionInternal()); + } else { + sb.append(versionRange.toString()); + } + if (scope != null) { + sb.append(':'); + sb.append(scope); + } + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultArtifact that = (DefaultArtifact) o; + return Objects.equals(groupId, that.groupId) + && Objects.equals(artifactId, that.artifactId) + && Objects.equals(type, that.type) + && Objects.equals(classifier, that.classifier) + && Objects.equals(version, that.version); + } + + @Override + public int hashCode() { + return Objects.hash(groupId, artifactId, type, classifier, version); + } + + @Override + public String getBaseVersion() { + if (baseVersion == null && version != null) { + setBaseVersionInternal(version); + } + + return baseVersion; + } + + protected String getBaseVersionInternal() { + if ((baseVersion == null) && (version != null)) { + setBaseVersionInternal(version); + } + + return baseVersion; + } + + @Override + public void setBaseVersion(String baseVersion) { + setBaseVersionInternal(baseVersion); + } + + protected void setBaseVersionInternal(String baseVersion) { + this.baseVersion = ArtifactUtils.toSnapshotVersion(baseVersion); + } + + @Override + public int compareTo(Artifact a) { + int result = groupId.compareTo(a.getGroupId()); + if (result == 0) { + result = artifactId.compareTo(a.getArtifactId()); + if (result == 0) { + result = type.compareTo(a.getType()); + if (result == 0) { + if (classifier == null) { + if (a.getClassifier() != null) { + result = 1; + } + } else { + if (a.getClassifier() != null) { + result = classifier.compareTo(a.getClassifier()); + } else { + result = -1; + } + } + if (result == 0) { + // We don't consider the version range in the comparison, just the resolved version + result = new DefaultArtifactVersion(version) + .compareTo(new DefaultArtifactVersion(a.getVersion())); + } + } + } + } + return result; + } + + @Override + public void updateVersion(String version, ArtifactRepository localRepository) { + setResolvedVersion(version); + setFile(new File(localRepository.getBasedir(), localRepository.pathOf(this))); + } + + @Override + public String getDownloadUrl() { + return downloadUrl; + } + + @Override + public void setDownloadUrl(String downloadUrl) { + this.downloadUrl = downloadUrl; + } + + @Override + public ArtifactFilter getDependencyFilter() { + return dependencyFilter; + } + + @Override + public void setDependencyFilter(ArtifactFilter artifactFilter) { + dependencyFilter = artifactFilter; + } + + @Override + public ArtifactHandler getArtifactHandler() { + return artifactHandler; + } + + @Override + public List getDependencyTrail() { + return dependencyTrail; + } + + @Override + public void setDependencyTrail(List dependencyTrail) { + this.dependencyTrail = dependencyTrail; + } + + @Override + public void setScope(String scope) { + this.scope = scope; + } + + @Override + public VersionRange getVersionRange() { + return versionRange; + } + + @Override + public void setVersionRange(VersionRange versionRange) { + this.versionRange = versionRange; + selectVersionFromNewRangeIfAvailable(); + } + + private void selectVersionFromNewRangeIfAvailable() { + if ((versionRange != null) && (versionRange.getRecommendedVersion() != null)) { + selectVersion(versionRange.getRecommendedVersion().toString()); + } else { + version = null; + baseVersion = null; + } + } + + @Override + public void selectVersion(String version) { + this.version = version; + setBaseVersionInternal(version); + } + + @Override + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + @Override + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + @Override + public boolean isSnapshot() { + return getBaseVersion() != null + && (getBaseVersion().endsWith(SNAPSHOT_VERSION) + || getBaseVersion().equals(LATEST_VERSION)); + } + + @Override + public void setResolved(boolean resolved) { + this.resolved = resolved; + } + + @Override + public boolean isResolved() { + return resolved; + } + + @Override + public void setResolvedVersion(String version) { + this.version = version; + // retain baseVersion + } + + @Override + public void setArtifactHandler(ArtifactHandler artifactHandler) { + this.artifactHandler = artifactHandler; + } + + @Override + public void setRelease(boolean release) { + this.release = release; + } + + @Override + public boolean isRelease() { + return release; + } + + @Override + public List getAvailableVersions() { + return availableVersions; + } + + @Override + public void setAvailableVersions(List availableVersions) { + this.availableVersions = availableVersions; + } + + @Override + public boolean isOptional() { + return optional; + } + + @Override + public ArtifactVersion getSelectedVersion() throws OverConstrainedVersionException { + return versionRange.getSelectedVersion(this); + } + + @Override + public boolean isSelectedVersionKnown() throws OverConstrainedVersionException { + return versionRange.isSelectedVersionKnown(this); + } + + @Override + public void setOptional(boolean optional) { + this.optional = optional; + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/InvalidArtifactRTException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/InvalidArtifactRTException.java new file mode 100644 index 000000000000..63cf7c36c631 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/InvalidArtifactRTException.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +/** + * Exception thrown when the identity of an artifact can not be established, + * e.g. one of groupId, artifactId, version or type is null. + */ +public class InvalidArtifactRTException extends RuntimeException { + + private final String groupId; + private final String artifactId; + private final String version; + private final String type; + private final String baseMessage; + + public InvalidArtifactRTException(String groupId, String artifactId, String version, String type, String message) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.type = type; + this.baseMessage = message; + } + + public InvalidArtifactRTException( + String groupId, String artifactId, String version, String type, String message, Throwable cause) { + super(cause); + + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.type = type; + this.baseMessage = message; + } + + @Override + public String getMessage() { + return "For artifact {" + getArtifactKey() + "}: " + getBaseMessage(); + } + + public String getBaseMessage() { + return baseMessage; + } + + public String getArtifactId() { + return artifactId; + } + + public String getGroupId() { + return groupId; + } + + public String getType() { + return type; + } + + public String getVersion() { + return version; + } + + public String getArtifactKey() { + return groupId + ":" + artifactId + ":" + version + ":" + type; + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java new file mode 100644 index 000000000000..12f50ef31a76 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.handler; + +/** + * An artifact handler contains information explaining how an artifact plugs into the Maven build:
      + *
    • Information needed to find the artifact file in a repository including extension and classifier
    • + *
    • Information on how to use the artifact as a dependency: whether to add it to the classpath, whether to load its + * dependencies transitively
    • + *
    + * + */ +public interface ArtifactHandler { + @Deprecated + String ROLE = ArtifactHandler.class.getName(); + + /** + * Returns the file name extension of the artifact; + * e.g. "jar", "pom", "xml", etc. + * + * @return the file extension + */ + String getExtension(); + + String getDirectory(); + + /** + * Returns the default classifier used if a different one is not set in pom.xml. + * + * @return the classifier + */ + String getClassifier(); + + String getPackaging(); + + boolean isIncludesDependencies(); + + String getLanguage(); + + /** + * Specifies if the artifact contains java classes and can be added to the classpath. + * Whether the artifact should be added to the classpath depends on other + * dependency properties. + * + * @return if the artifact can be added to the class path + * + * @deprecated A value of {@code true} does not mean that the dependency should + * be placed on the classpath. See {@code JavaPathType} instead for better analysis. + */ + @Deprecated + boolean isAddedToClasspath(); +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadata.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadata.java new file mode 100644 index 000000000000..fd079999a88f --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadata.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +/** + * Contains metadata about an artifact, and methods to retrieve/store it from an artifact repository. + */ +@Deprecated +public interface ArtifactMetadata extends org.apache.maven.repository.legacy.metadata.ArtifactMetadata { + void merge(ArtifactMetadata metadata); +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/ArtifactRepository.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/ArtifactRepository.java new file mode 100644 index 000000000000..f8459d55a7b3 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/ArtifactRepository.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.repository.Proxy; + +/** + * Abstraction of an artifact repository. Artifact repositories can be remote, local, or even build reactor or + * IDE workspace. + * + * @deprecated Avoid use of this type, if you need access to local repository use repository system classes instead. + */ +@Deprecated +public interface ArtifactRepository { + String pathOf(Artifact artifact); + + String pathOfRemoteRepositoryMetadata(ArtifactMetadata artifactMetadata); + + String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository); + + String getUrl(); + + void setUrl(String url); + + String getBasedir(); + + default Path getBasedirPath() { + return Paths.get(getBasedir()); + } + + String getProtocol(); + + String getId(); + + void setId(String id); + + ArtifactRepositoryPolicy getSnapshots(); + + void setSnapshotUpdatePolicy(ArtifactRepositoryPolicy policy); + + ArtifactRepositoryPolicy getReleases(); + + void setReleaseUpdatePolicy(ArtifactRepositoryPolicy policy); + + ArtifactRepositoryLayout getLayout(); + + void setLayout(ArtifactRepositoryLayout layout); + + String getKey(); + + @Deprecated + boolean isUniqueVersion(); + + @Deprecated + boolean isBlacklisted(); + + @Deprecated + void setBlacklisted(boolean blackListed); + + /** + * @return whether the repository is blocked + * @since 3.8.1 + **/ + boolean isBlocked(); + + /** + * @param blocked block the repository? + * @since 3.8.1 + **/ + void setBlocked(boolean blocked); + + // + // New interface methods for the repository system. + // + /** + * + * @param artifact an artifact + * @return found artifact + * @since 3.0-alpha-3 + */ + Artifact find(Artifact artifact); + + /** + * Finds the versions of the specified artifact that are available in this repository. + * + * @param artifact The artifact whose available versions should be determined, must not be {@code null}. + * @return The available versions of the artifact or an empty list if none, never {@code null}. + * @since 3.0-alpha-3 + */ + List findVersions(Artifact artifact); + + /** + * Indicates whether this repository is backed by actual projects. For instance, the build reactor or IDE workspace + * are examples of such repositories. + * + * @return {@code true} if the repository is backed by actual projects, {@code false} otherwise. + * @since 3.0-beta-1 + */ + boolean isProjectAware(); + + /** + * @param authentication authentication + * @since 3.0-alpha-3 + */ + void setAuthentication(Authentication authentication); + + /** + * @return repository authentication + * @since 3.0-alpha-3 + */ + Authentication getAuthentication(); + + /** + * @param proxy proxy + * @since 3.0-alpha-3 + */ + void setProxy(Proxy proxy); + + /** + * @since 3.0-alpha-3 + * @return repository proxy + */ + Proxy getProxy(); + + /** + * @since 3.0.3 + * @return the repositories mirrored by the actual one + */ + List getMirroredRepositories(); + + /** + * @since 3.0.3 + * @param mirroredRepositories the repositories that the actual one mirrors + */ + void setMirroredRepositories(List mirroredRepositories); +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/ArtifactRepositoryPolicy.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/ArtifactRepositoryPolicy.java new file mode 100644 index 000000000000..8ab3ddca7b0f --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/ArtifactRepositoryPolicy.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import java.util.Calendar; +import java.util.Date; + +/** + * Describes a set of policies for a repository to use under certain conditions. + * + * @deprecated Avoid use of this type, if you need access to local repository use repository system session instead. + */ +@Deprecated +public class ArtifactRepositoryPolicy { + public static final String UPDATE_POLICY_NEVER = "never"; + + public static final String UPDATE_POLICY_ALWAYS = "always"; + + public static final String UPDATE_POLICY_DAILY = "daily"; + + public static final String UPDATE_POLICY_INTERVAL = "interval"; + + public static final String CHECKSUM_POLICY_FAIL = "fail"; + + public static final String CHECKSUM_POLICY_WARN = "warn"; + + public static final String CHECKSUM_POLICY_IGNORE = "ignore"; + + public static final String DEFAULT_CHECKSUM_POLICY = CHECKSUM_POLICY_FAIL; + + private boolean enabled; + + private String updatePolicy; + + private String checksumPolicy; + + public ArtifactRepositoryPolicy() { + this(true, null, null); + } + + public ArtifactRepositoryPolicy(ArtifactRepositoryPolicy policy) { + this(policy.isEnabled(), policy.getUpdatePolicy(), policy.getChecksumPolicy()); + } + + public ArtifactRepositoryPolicy(boolean enabled, String updatePolicy, String checksumPolicy) { + this.enabled = enabled; + + if (updatePolicy == null) { + updatePolicy = UPDATE_POLICY_DAILY; + } + this.updatePolicy = updatePolicy; + + if (checksumPolicy == null) { + checksumPolicy = DEFAULT_CHECKSUM_POLICY; + } + this.checksumPolicy = checksumPolicy; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public void setUpdatePolicy(String updatePolicy) { + if (updatePolicy != null) { + this.updatePolicy = updatePolicy; + } + } + + public void setChecksumPolicy(String checksumPolicy) { + if (checksumPolicy != null) { + this.checksumPolicy = checksumPolicy; + } + } + + public boolean isEnabled() { + return enabled; + } + + public String getUpdatePolicy() { + return updatePolicy; + } + + public String getChecksumPolicy() { + return checksumPolicy; + } + + public boolean checkOutOfDate(Date lastModified) { + boolean checkForUpdates = false; + + if (UPDATE_POLICY_ALWAYS.equals(updatePolicy)) { + checkForUpdates = true; + } else if (UPDATE_POLICY_DAILY.equals(updatePolicy)) { + // Get local midnight boundary + Calendar cal = Calendar.getInstance(); + + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + if (cal.getTime().after(lastModified)) { + checkForUpdates = true; + } + } else if (updatePolicy.startsWith(UPDATE_POLICY_INTERVAL)) { + String s = updatePolicy.substring(UPDATE_POLICY_INTERVAL.length() + 1); + int minutes = Integer.parseInt(s); + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.MINUTE, -minutes); + if (cal.getTime().after(lastModified)) { + checkForUpdates = true; + } + } + // else assume "never" + return checkForUpdates; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(64); + buffer.append("{enabled="); + buffer.append(enabled); + buffer.append(", checksums="); + buffer.append(checksumPolicy); + buffer.append(", updates="); + buffer.append(updatePolicy); + buffer.append('}'); + return buffer.toString(); + } + + public void merge(ArtifactRepositoryPolicy policy) { + if (policy != null && policy.isEnabled()) { + setEnabled(true); + + if (ordinalOfCksumPolicy(policy.getChecksumPolicy()) < ordinalOfCksumPolicy(getChecksumPolicy())) { + setChecksumPolicy(policy.getChecksumPolicy()); + } + + if (ordinalOfUpdatePolicy(policy.getUpdatePolicy()) < ordinalOfUpdatePolicy(getUpdatePolicy())) { + setUpdatePolicy(policy.getUpdatePolicy()); + } + } + } + + private int ordinalOfCksumPolicy(String policy) { + if (ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(policy)) { + return 2; + } else if (ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(policy)) { + return 0; + } else { + return 1; + } + } + + private int ordinalOfUpdatePolicy(String policy) { + if (ArtifactRepositoryPolicy.UPDATE_POLICY_DAILY.equals(policy)) { + return 1440; + } else if (ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS.equals(policy)) { + return 0; + } else if (policy != null && policy.startsWith(ArtifactRepositoryPolicy.UPDATE_POLICY_INTERVAL)) { + String s = policy.substring(UPDATE_POLICY_INTERVAL.length() + 1); + return Integer.parseInt(s); + } else { + return Integer.MAX_VALUE; + } + } +} diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java similarity index 83% rename from maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java rename to compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java index f02d1c5d7f86..62d0165c5495 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,25 +16,24 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.repository; /** * Authentication */ -public class Authentication -{ +public class Authentication { private String privateKey; private String passphrase; - public Authentication( String userName, String password ) - { + public Authentication(String userName, String password) { this.username = userName; this.password = password; } /** - * Username used to login to the host + * Username used to log in to the host */ private String username; @@ -50,8 +47,7 @@ public Authentication( String userName, String password ) * * @return password of user */ - public String getPassword() - { + public String getPassword() { return password; } @@ -60,8 +56,7 @@ public String getPassword() * * @param password password of the user */ - public void setPassword( String password ) - { + public void setPassword(String password) { this.password = password; } @@ -70,8 +65,7 @@ public void setPassword( String password ) * * @return username at repository */ - public String getUsername() - { + public String getUsername() { return username; } @@ -80,8 +74,7 @@ public String getUsername() * * @param userName the username used to access repository */ - public void setUsername( final String userName ) - { + public void setUsername(final String userName) { this.username = userName; } @@ -91,8 +84,7 @@ public void setUsername( final String userName ) * * @return passphrase of the private key file */ - public String getPassphrase() - { + public String getPassphrase() { return passphrase; } @@ -101,8 +93,7 @@ public String getPassphrase() * * @param passphrase passphrase of the private key file */ - public void setPassphrase( final String passphrase ) - { + public void setPassphrase(final String passphrase) { this.passphrase = passphrase; } @@ -111,8 +102,7 @@ public void setPassphrase( final String passphrase ) * * @return absolute path to private key */ - public String getPrivateKey() - { + public String getPrivateKey() { return privateKey; } @@ -121,9 +111,7 @@ public String getPrivateKey() * * @param privateKey path to private key in local file system */ - public void setPrivateKey( final String privateKey ) - { + public void setPrivateKey(final String privateKey) { this.privateKey = privateKey; } - } diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout.java new file mode 100644 index 000000000000..ec0097620cf0 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.layout; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * Repository layout. + * + * @deprecated Avoid use of this type, if you need access to local repository use repository system session instead. + */ +@Deprecated +public interface ArtifactRepositoryLayout { + String ROLE = ArtifactRepositoryLayout.class.getName(); + + String getId(); + + String pathOf(Artifact artifact); + + String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository); + + String pathOfRemoteRepositoryMetadata(ArtifactMetadata metadata); +} diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout2.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout2.java similarity index 75% rename from maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout2.java rename to compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout2.java index f6bc573005ef..8cdc09d8a9d5 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout2.java +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/layout/ArtifactRepositoryLayout2.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.repository.layout; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.repository.layout; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; @@ -25,10 +24,8 @@ /** * ArtifactRepositoryLayout2 */ -public interface ArtifactRepositoryLayout2 - extends ArtifactRepositoryLayout -{ +public interface ArtifactRepositoryLayout2 extends ArtifactRepositoryLayout { - ArtifactRepository newMavenArtifactRepository( String id, String url, ArtifactRepositoryPolicy snapshots, - ArtifactRepositoryPolicy releases ); + ArtifactRepository newMavenArtifactRepository( + String id, String url, ArtifactRepositoryPolicy snapshots, ArtifactRepositoryPolicy releases); } diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataStoreException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataStoreException.java new file mode 100644 index 000000000000..0db5434cf75b --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataStoreException.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +/** + * Problem storing the repository metadata in the local repository. + * + */ +public class RepositoryMetadataStoreException extends Exception { + public RepositoryMetadataStoreException(String message) { + super(message); + } + + public RepositoryMetadataStoreException(String message, Exception e) { + super(message, e); + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/AbstractArtifactResolutionException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/AbstractArtifactResolutionException.java new file mode 100644 index 000000000000..7af0a3e3f57d --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/AbstractArtifactResolutionException.java @@ -0,0 +1,323 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; + +/** + * Base class for artifact resolution exceptions. + * + */ +public class AbstractArtifactResolutionException extends Exception { + private String groupId; + + private String artifactId; + + private String version; + + private String type; + + private String classifier; + + private Artifact artifact; + + private List remoteRepositories; + + private final String originalMessage; + + private final String path; + + static final String LS = System.lineSeparator(); + + @SuppressWarnings("checkstyle:parameternumber") + protected AbstractArtifactResolutionException( + String message, + String groupId, + String artifactId, + String version, + String type, + String classifier, + List remoteRepositories, + List path) { + this(message, groupId, artifactId, version, type, classifier, remoteRepositories, path, null); + } + + @SuppressWarnings("checkstyle:parameternumber") + protected AbstractArtifactResolutionException( + String message, + String groupId, + String artifactId, + String version, + String type, + String classifier, + List remoteRepositories, + List path, + Throwable t) { + super(constructMessageBase(message, groupId, artifactId, version, type, remoteRepositories, path), t); + + this.originalMessage = message; + this.groupId = groupId; + this.artifactId = artifactId; + this.type = type; + this.classifier = classifier; + this.version = version; + this.remoteRepositories = remoteRepositories; + this.path = constructArtifactPath(path, ""); + } + + protected AbstractArtifactResolutionException(String message, Artifact artifact) { + this(message, artifact, null); + } + + protected AbstractArtifactResolutionException( + String message, Artifact artifact, List remoteRepositories) { + this(message, artifact, remoteRepositories, null); + } + + protected AbstractArtifactResolutionException( + String message, Artifact artifact, List remoteRepositories, Throwable t) { + this( + message, + artifact.getGroupId(), + artifact.getArtifactId(), + artifact.getVersion(), + artifact.getType(), + artifact.getClassifier(), + remoteRepositories, + artifact.getDependencyTrail(), + t); + this.artifact = artifact; + } + + public Artifact getArtifact() { + return artifact; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getVersion() { + return version; + } + + public String getType() { + return type; + } + + /** @return the classifier */ + public String getClassifier() { + return this.classifier; + } + + /** @return the path */ + public String getPath() { + return this.path; + } + + public List getRemoteRepositories() { + return remoteRepositories; + } + + public String getOriginalMessage() { + return originalMessage; + } + + protected static String constructArtifactPath(List path, String indentation) { + StringBuilder sb = new StringBuilder(); + + if (path != null) { + sb.append(LS); + sb.append(indentation); + sb.append("Path to dependency: "); + sb.append(LS); + int num = 1; + for (Iterator i = path.iterator(); i.hasNext(); num++) { + sb.append(indentation); + sb.append('\t'); + sb.append(num); + sb.append(") "); + sb.append(i.next()); + sb.append(LS); + } + } + + return sb.toString(); + } + + private static String constructMessageBase( + String message, + String groupId, + String artifactId, + String version, + String type, + List remoteRepositories, + List path) { + StringBuilder sb = new StringBuilder(); + + sb.append(message); + + if (message == null || !message.contains("from the specified remote repositories:")) { + sb.append(LS); + sb.append(" ") + .append(groupId) + .append(':') + .append(artifactId) + .append(':') + .append(type) + .append(':') + .append(version); + sb.append(LS); + if (remoteRepositories != null) { + sb.append(LS); + sb.append("from the specified remote repositories:"); + sb.append(LS).append(" "); + + if (remoteRepositories.isEmpty()) { + sb.append("(none)"); + } + + for (Iterator i = remoteRepositories.iterator(); i.hasNext(); ) { + ArtifactRepository remoteRepository = i.next(); + + sb.append(remoteRepository.getId()); + sb.append(" ("); + sb.append(remoteRepository.getUrl()); + + ArtifactRepositoryPolicy releases = remoteRepository.getReleases(); + if (releases != null) { + sb.append(", releases=").append(releases.isEnabled()); + } + + ArtifactRepositoryPolicy snapshots = remoteRepository.getSnapshots(); + if (snapshots != null) { + sb.append(", snapshots=").append(snapshots.isEnabled()); + } + + sb.append(')'); + if (i.hasNext()) { + sb.append(',').append(LS).append(" "); + } + } + } + + sb.append(constructArtifactPath(path, "")); + sb.append(LS); + } + + return sb.toString(); + } + + @SuppressWarnings("checkstyle:parameternumber") + protected static String constructMissingArtifactMessage( + String message, + String indentation, + String groupId, + String artifactId, + String version, + String type, + String classifier, + String downloadUrl, + List path) { + StringBuilder sb = new StringBuilder(message); + + if (!"pom".equals(type)) { + if (downloadUrl != null) { + sb.append(LS); + sb.append(LS); + sb.append(indentation); + sb.append("Try downloading the file manually from: "); + sb.append(LS); + sb.append(indentation); + sb.append(" "); + sb.append(downloadUrl); + } else { + sb.append(LS); + sb.append(LS); + sb.append(indentation); + sb.append("Try downloading the file manually from the project website."); + } + + sb.append(LS); + sb.append(LS); + sb.append(indentation); + sb.append("Then, install it using the command: "); + sb.append(LS); + sb.append(indentation); + sb.append(" mvn install:install-file -DgroupId="); + sb.append(groupId); + sb.append(" -DartifactId="); + sb.append(artifactId); + sb.append(" -Dversion="); + sb.append(version); + + // insert classifier only if it was used in the artifact + if (classifier != null && !classifier.isEmpty()) { + sb.append(" -Dclassifier="); + sb.append(classifier); + } + sb.append(" -Dpackaging="); + sb.append(type); + sb.append(" -Dfile=/path/to/file"); + sb.append(LS); + + // If people want to deploy it + sb.append(LS); + sb.append(indentation); + sb.append("Alternatively, if you host your own repository you can deploy the file there: "); + sb.append(LS); + sb.append(indentation); + sb.append(" mvn deploy:deploy-file -DgroupId="); + sb.append(groupId); + sb.append(" -DartifactId="); + sb.append(artifactId); + sb.append(" -Dversion="); + sb.append(version); + + // insert classifier only if it was used in the artifact + if (classifier != null && !classifier.isEmpty()) { + sb.append(" -Dclassifier="); + sb.append(classifier); + } + sb.append(" -Dpackaging="); + sb.append(type); + sb.append(" -Dfile=/path/to/file"); + sb.append(" -Durl=[url] -DrepositoryId=[id]"); + sb.append(LS); + } + + sb.append(constructArtifactPath(path, indentation)); + sb.append(LS); + + return sb.toString(); + } + + public String getArtifactPath() { + return path; + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/ArtifactNotFoundException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/ArtifactNotFoundException.java new file mode 100644 index 000000000000..f9f706b8e0f3 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/ArtifactNotFoundException.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + */ +public class ArtifactNotFoundException extends AbstractArtifactResolutionException { + private String downloadUrl; + + protected ArtifactNotFoundException( + String message, Artifact artifact, List remoteRepositories) { + super(message, artifact, remoteRepositories); + } + + public ArtifactNotFoundException(String message, Artifact artifact) { + this( + message, + artifact.getGroupId(), + artifact.getArtifactId(), + artifact.getVersion(), + artifact.getType(), + artifact.getClassifier(), + null, + artifact.getDownloadUrl(), + artifact.getDependencyTrail()); + } + + protected ArtifactNotFoundException( + String message, Artifact artifact, List remoteRepositories, Throwable cause) { + this( + message, + artifact.getGroupId(), + artifact.getArtifactId(), + artifact.getVersion(), + artifact.getType(), + artifact.getClassifier(), + remoteRepositories, + artifact.getDownloadUrl(), + artifact.getDependencyTrail(), + cause); + } + + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactNotFoundException( + String message, + String groupId, + String artifactId, + String version, + String type, + String classifier, + List remoteRepositories, + String downloadUrl, + List path, + Throwable cause) { + super( + constructMissingArtifactMessage( + message, "", groupId, artifactId, version, type, classifier, downloadUrl, path), + groupId, + artifactId, + version, + type, + classifier, + remoteRepositories, + null, + cause); + + this.downloadUrl = downloadUrl; + } + + @SuppressWarnings("checkstyle:parameternumber") + private ArtifactNotFoundException( + String message, + String groupId, + String artifactId, + String version, + String type, + String classifier, + List remoteRepositories, + String downloadUrl, + List path) { + super( + constructMissingArtifactMessage( + message, "", groupId, artifactId, version, type, classifier, downloadUrl, path), + groupId, + artifactId, + version, + type, + classifier, + remoteRepositories, + null); + + this.downloadUrl = downloadUrl; + } + + public String getDownloadUrl() { + return downloadUrl; + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionException.java new file mode 100644 index 000000000000..2326d2f39beb --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionException.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + */ +public class ArtifactResolutionException extends AbstractArtifactResolutionException { + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactResolutionException( + String message, + String groupId, + String artifactId, + String version, + String type, + String classifier, + List remoteRepositories, + List path, + Throwable t) { + super(message, groupId, artifactId, version, type, classifier, remoteRepositories, path, t); + } + + public ArtifactResolutionException( + String message, + String groupId, + String artifactId, + String version, + String type, + String classifier, + Throwable t) { + super(message, groupId, artifactId, version, type, classifier, null, null, t); + } + + public ArtifactResolutionException(String message, Artifact artifact) { + super(message, artifact); + } + + public ArtifactResolutionException(String message, Artifact artifact, List remoteRepositories) { + super(message, artifact, remoteRepositories); + } + + public ArtifactResolutionException(String message, Artifact artifact, Throwable cause) { + super(message, artifact, null, cause); + } + + public ArtifactResolutionException( + String message, Artifact artifact, List remoteRepositories, Throwable cause) { + super(message, artifact, remoteRepositories, cause); + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/CyclicDependencyException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/CyclicDependencyException.java new file mode 100644 index 000000000000..e04c6b2a1e3a --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/CyclicDependencyException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import org.apache.maven.artifact.Artifact; + +/** + * Indicates a cycle in the dependency graph. + * + */ +public class CyclicDependencyException extends ArtifactResolutionException { + private Artifact artifact; + + public CyclicDependencyException(String message, Artifact artifact) { + super(message, artifact); + this.artifact = artifact; + } + + @Override + public Artifact getArtifact() { + return artifact; + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/MultipleArtifactsNotFoundException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/MultipleArtifactsNotFoundException.java new file mode 100644 index 000000000000..96d8c50362bc --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/MultipleArtifactsNotFoundException.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * Exception caused when one or more artifacts can not be resolved because they are not found in the + * local or remote repositories. + */ +public class MultipleArtifactsNotFoundException extends ArtifactResolutionException { + private static final String LS = System.lineSeparator(); + + private final List resolvedArtifacts; + private final List missingArtifacts; + + /** + * @param originatingArtifact the artifact that was being resolved + * @param missingArtifacts artifacts that could not be resolved + * @param remoteRepositories remote repositories where the missing artifacts were not found + * @deprecated use {@link #MultipleArtifactsNotFoundException(Artifact, List, List, List)} + */ + @Deprecated + public MultipleArtifactsNotFoundException( + Artifact originatingArtifact, + List missingArtifacts, + List remoteRepositories) { + this(originatingArtifact, new ArrayList<>(), missingArtifacts, remoteRepositories); + } + + /** + * Create an instance of the exception with all required information. + * + * @param originatingArtifact the artifact that was being resolved + * @param resolvedArtifacts artifacts that could be resolved + * @param missingArtifacts artifacts that could not be resolved + * @param remoteRepositories remote repositories where the missing artifacts were not found + */ + public MultipleArtifactsNotFoundException( + Artifact originatingArtifact, + List resolvedArtifacts, + List missingArtifacts, + List remoteRepositories) { + super(constructMessage(missingArtifacts), originatingArtifact, remoteRepositories); + this.resolvedArtifacts = resolvedArtifacts; + this.missingArtifacts = missingArtifacts; + } + + /** + * artifacts that could be resolved + * + * @return {@link List} of {@link Artifact} + */ + public List getResolvedArtifacts() { + return resolvedArtifacts; + } + + /** + * artifacts that could NOT be resolved + * + * @return {@link List} of {@link Artifact} + */ + public List getMissingArtifacts() { + return missingArtifacts; + } + + private static String constructMessage(List artifacts) { + StringBuilder buffer = new StringBuilder(256); + + buffer.append("Missing:").append(LS); + buffer.append("----------").append(LS); + + int counter = 0; + + for (Artifact artifact : artifacts) { + String message = (++counter) + ") " + artifact.getId(); + + buffer.append(constructMissingArtifactMessage( + message, + " ", + artifact.getGroupId(), + artifact.getArtifactId(), + artifact.getVersion(), + artifact.getType(), + artifact.getClassifier(), + artifact.getDownloadUrl(), + artifact.getDependencyTrail())); + } + + buffer.append("----------").append(LS); + + int size = artifacts.size(); + + buffer.append(size).append(" required artifact"); + + if (size > 1) { + buffer.append("s are"); + } else { + buffer.append(" is"); + } + + buffer.append(" missing.").append(LS).append(LS).append("for artifact: "); + + return buffer.toString(); + } +} diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/filter/ArtifactFilter.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/filter/ArtifactFilter.java similarity index 82% rename from maven-artifact/src/main/java/org/apache/maven/artifact/resolver/filter/ArtifactFilter.java rename to compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/filter/ArtifactFilter.java index 27b08a88c4bc..c28331b1a96a 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/filter/ArtifactFilter.java +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/resolver/filter/ArtifactFilter.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.resolver.filter; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,13 +16,12 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.resolver.filter; import org.apache.maven.artifact.Artifact; /** - * @author Jason van Zyl */ -public interface ArtifactFilter -{ - boolean include( Artifact artifact ); +public interface ArtifactFilter { + boolean include(Artifact artifact); } diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ArtifactVersion.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ArtifactVersion.java similarity index 82% rename from maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ArtifactVersion.java rename to compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ArtifactVersion.java index 5b516a91d016..176af917c123 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ArtifactVersion.java +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ArtifactVersion.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.versioning; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,16 +16,14 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.versioning; /** * Describes an artifact version in terms of its components, converts it to/from a string and * compares two versions. * - * @author Brett Porter */ -public interface ArtifactVersion - extends Comparable -{ +public interface ArtifactVersion extends Comparable { int getMajorVersion(); int getMinorVersion(); @@ -38,5 +34,5 @@ public interface ArtifactVersion String getQualifier(); - void parseVersion( String version ); + void parseVersion(String version); } diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java new file mode 100644 index 000000000000..df3c0345288f --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java @@ -0,0 +1,854 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.math.BigInteger; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Properties; + +/** + *

    + * Generic implementation of version comparison. + *

    + *

    + * Features: + *

      + *
    • Mixing of '-' (hyphen) and '.' (dot) separators,
    • + *
    • Transition between characters and digits also constitutes a separator: + * 1.0alpha1 => [1, [alpha, 1]]
    • + *
    • Unlimited number of version components,
    • + *
    • Version components in the text can be digits or strings,
    • + *
    • Strings are checked for well-known qualifiers, and the qualifier ordering is used for version ordering. + * Well-known qualifiers (case-insensitive) are, in order from least to greatest:
        + *
      1. alpha or a
      2. + *
      3. beta or b
      4. + *
      5. milestone or m
      6. + *
      7. rc or cr
      8. + *
      9. snapshot
      10. + *
      11. ga or final
      12. + *
      13. sp
      14. + *
      + * Unknown qualifiers are considered after known qualifiers, + * with lexical order (case-insensitive in the English locale). + * ga and final sort the same as not having a qualifier. + *
    • + *
    • A hyphen usually precedes a qualifier, and is always less important than digits/number. For example + * {@code 1.0.RC2 < 1.0-RC3 < 1.0.1}; but prefer {@code 1.0.0-RC2} over {@code 1.0.0.RC2}, and more + * generally: {@code 1.0.X2 < 1.0-X3 < 1.0.1} for any string {@code X}; but prefer {@code 1.0.0-X1} + * over {@code 1.0.0.X1}.
    • + *
    + * + * @see "Versioning" in the POM reference + */ +public class ComparableVersion implements Comparable { + private static final int MAX_INTITEM_LENGTH = 9; + + private static final int MAX_LONGITEM_LENGTH = 18; + + private String value; + + private String canonical; + + private ListItem items; + + private interface Item { + int INT_ITEM = 3; + int LONG_ITEM = 4; + int BIGINTEGER_ITEM = 0; + int STRING_ITEM = 1; + int LIST_ITEM = 2; + int COMBINATION_ITEM = 5; + + int compareTo(Item item); + + int getType(); + + boolean isNull(); + } + + /** + * Represents a numeric item in the version item list that can be represented with an int. + */ + private static class IntItem implements Item { + private final int value; + + public static final IntItem ZERO = new IntItem(); + + private IntItem() { + this.value = 0; + } + + IntItem(String str) { + this.value = Integer.parseInt(str); + } + + @Override + public int getType() { + return INT_ITEM; + } + + @Override + public boolean isNull() { + return value == 0; + } + + @Override + public int compareTo(Item item) { + if (item == null) { + return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + return switch (item.getType()) { + case INT_ITEM -> { + int itemValue = ((IntItem) item).value; + yield Integer.compare(value, itemValue); + } + case LONG_ITEM, BIGINTEGER_ITEM -> -1; + case STRING_ITEM -> 1; + case COMBINATION_ITEM -> 1; // 1.1 > 1-sp + + case LIST_ITEM -> 1; // 1.1 > 1-1 + + default -> throw new IllegalStateException("invalid item: " + item.getClass()); + }; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + IntItem intItem = (IntItem) o; + + return value == intItem.value; + } + + @Override + public int hashCode() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } + } + + /** + * Represents a numeric item in the version item list that can be represented with a long. + */ + private static class LongItem implements Item { + private final long value; + + LongItem(String str) { + this.value = Long.parseLong(str); + } + + @Override + public int getType() { + return LONG_ITEM; + } + + @Override + public boolean isNull() { + return value == 0; + } + + @Override + public int compareTo(Item item) { + if (item == null) { + return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + return switch (item.getType()) { + case INT_ITEM -> 1; + case LONG_ITEM -> { + long itemValue = ((LongItem) item).value; + yield Long.compare(value, itemValue); + } + case BIGINTEGER_ITEM -> -1; + case STRING_ITEM -> 1; + case COMBINATION_ITEM -> 1; // 1.1 > 1-sp + + case LIST_ITEM -> 1; // 1.1 > 1-1 + + default -> throw new IllegalStateException("invalid item: " + item.getClass()); + }; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + LongItem longItem = (LongItem) o; + + return value == longItem.value; + } + + @Override + public int hashCode() { + return (int) (value ^ (value >>> 32)); + } + + @Override + public String toString() { + return Long.toString(value); + } + } + + /** + * Represents a numeric item in the version item list. + */ + private static class BigIntegerItem implements Item { + private final BigInteger value; + + BigIntegerItem(String str) { + this.value = new BigInteger(str); + } + + @Override + public int getType() { + return BIGINTEGER_ITEM; + } + + @Override + public boolean isNull() { + return BigInteger.ZERO.equals(value); + } + + @Override + public int compareTo(Item item) { + if (item == null) { + return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + return switch (item.getType()) { + case INT_ITEM, LONG_ITEM -> 1; + case BIGINTEGER_ITEM -> value.compareTo(((BigIntegerItem) item).value); + case STRING_ITEM -> 1; + case COMBINATION_ITEM -> 1; // 1.1 > 1-sp + + case LIST_ITEM -> 1; // 1.1 > 1-1 + + default -> throw new IllegalStateException("invalid item: " + item.getClass()); + }; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BigIntegerItem that = (BigIntegerItem) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value.toString(); + } + } + + /** + * Represents a string in the version item list, usually a qualifier. + */ + private static class StringItem implements Item { + private static final List QUALIFIERS = + Arrays.asList("alpha", "beta", "milestone", "rc", "snapshot", "", "sp"); + private static final List RELEASE_QUALIFIERS = Arrays.asList("ga", "final", "release"); + + private static final Properties ALIASES = new Properties(); + + static { + ALIASES.put("cr", "rc"); + } + + /** + * A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes + * the version older than one without a qualifier, or more recent. + */ + private static final String RELEASE_VERSION_INDEX = String.valueOf(QUALIFIERS.indexOf("")); + + private final String value; + + StringItem(String value, boolean followedByDigit) { + if (followedByDigit && value.length() == 1) { + // a1 = alpha-1, b1 = beta-1, m1 = milestone-1 + switch (value.charAt(0)) { + case 'a': + value = "alpha"; + break; + case 'b': + value = "beta"; + break; + case 'm': + value = "milestone"; + break; + default: + } + } + this.value = ALIASES.getProperty(value, value); + } + + @Override + public int getType() { + return STRING_ITEM; + } + + @Override + public boolean isNull() { + return value == null || value.isEmpty(); + } + + /** + * Returns a comparable value for a qualifier. + *

    + * This method takes into account the ordering of known qualifiers then unknown qualifiers with lexical + * ordering. + *

    + * + * @param qualifier + * @return an equivalent value that can be used with lexical comparison + */ + public static String comparableQualifier(String qualifier) { + if (RELEASE_QUALIFIERS.contains(qualifier)) { + return String.valueOf(QUALIFIERS.indexOf("")); + } + + int i = QUALIFIERS.indexOf(qualifier); + + // Just returning an Integer with the index here is faster, but requires a lot of if/then/else to check for + // -1 + // or QUALIFIERS.size and then resort to lexical ordering. Most comparisons are decided by the first + // character, + // so this is still fast. If more characters are needed then it requires a lexical sort anyway. + return i == -1 ? (QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); + } + + @Override + public int compareTo(Item item) { + if (item == null) { + // 1-rc < 1, 1-ga > 1 + return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX); + } + switch (item.getType()) { + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; // 1.any < 1.1 ? + + case STRING_ITEM: + return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); + + case COMBINATION_ITEM: + int result = this.compareTo(((CombinationItem) item).getStringPart()); + if (result == 0) { + return -1; + } + return result; + + case LIST_ITEM: + return -1; // 1.any < 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + StringItem that = (StringItem) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; + } + } + + /** + * Represents a combination in the version item list. + * It is usually a combination of a string and a number, with the string first and the number second. + */ + private static class CombinationItem implements Item { + + StringItem stringPart; + + Item digitPart; + + CombinationItem(String value) { + int index = 0; + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (Character.isDigit(c)) { + index = i; + break; + } + } + + stringPart = new StringItem(value.substring(0, index), true); + digitPart = parseItem(true, value.substring(index)); + } + + @Override + public int compareTo(Item item) { + if (item == null) { + // 1-rc1 < 1, 1-ga1 > 1 + return stringPart.compareTo(item); + } + int result = 0; + switch (item.getType()) { + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; + + case STRING_ITEM: + result = stringPart.compareTo(item); + if (result == 0) { + // X1 > X + return 1; + } + return result; + + case LIST_ITEM: + return -1; + + case COMBINATION_ITEM: + result = stringPart.compareTo(((CombinationItem) item).getStringPart()); + if (result == 0) { + return digitPart.compareTo(((CombinationItem) item).getDigitPart()); + } + return result; + default: + return 0; + } + } + + public StringItem getStringPart() { + return stringPart; + } + + public Item getDigitPart() { + return digitPart; + } + + @Override + public int getType() { + return COMBINATION_ITEM; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CombinationItem that = (CombinationItem) o; + return Objects.equals(stringPart, that.stringPart) && Objects.equals(digitPart, that.digitPart); + } + + @Override + public int hashCode() { + return Objects.hash(stringPart, digitPart); + } + + @Override + public String toString() { + return stringPart.toString() + digitPart.toString(); + } + } + + /** + * Represents a version list item. This class is used both for the global item list and for sub-lists (which start + * with '-(number)' in the version specification). + */ + private static class ListItem extends ArrayList implements Item { + @Override + public int getType() { + return LIST_ITEM; + } + + @Override + public boolean isNull() { + return (size() == 0); + } + + void normalize() { + for (int i = size() - 1; i >= 0; i--) { + Item lastItem = get(i); + + if (lastItem.isNull()) { + if (i == size() - 1 || get(i + 1).getType() == STRING_ITEM) { + remove(i); + } else if (get(i + 1).getType() == LIST_ITEM) { + Item item = ((ListItem) get(i + 1)).get(0); + if (item.getType() == COMBINATION_ITEM || item.getType() == STRING_ITEM) { + remove(i); + } + } + } + } + } + + @Override + public int compareTo(Item item) { + if (item == null) { + if (size() == 0) { + return 0; // 1-0 = 1- (normalize) = 1 + } + // Compare the entire list of items with null - not just the first one, MNG-6964 + for (Item i : this) { + int result = i.compareTo(null); + if (result != 0) { + return result; + } + } + return 0; + } + switch (item.getType()) { + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; // 1-1 < 1.0.x + + case STRING_ITEM: + return 1; + case COMBINATION_ITEM: + return 1; // 1-1 > 1-sp + + case LIST_ITEM: + Iterator left = iterator(); + Iterator right = ((ListItem) item).iterator(); + + while (left.hasNext() || right.hasNext()) { + Item l = left.hasNext() ? left.next() : null; + Item r = right.hasNext() ? right.next() : null; + + // if this is shorter, then invert the compare and mul with -1 + int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); + + if (result != 0) { + return result; + } + } + + return 0; + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + for (Item item : this) { + if (!buffer.isEmpty()) { + buffer.append((item instanceof ListItem) ? '-' : '.'); + } + buffer.append(item); + } + return buffer.toString(); + } + + /** + * Return the contents in the same format that is used when you call toString() on a List. + */ + private String toListString() { + StringBuilder buffer = new StringBuilder(); + buffer.append("["); + for (Item item : this) { + if (buffer.length() > 1) { + buffer.append(", "); + } + if (item instanceof ListItem listItem) { + buffer.append(listItem.toListString()); + } else { + buffer.append(item); + } + } + buffer.append("]"); + return buffer.toString(); + } + } + + public ComparableVersion(String version) { + parseVersion(version); + } + + @SuppressWarnings("checkstyle:innerassignment") + public final void parseVersion(String version) { + this.value = version; + + items = new ListItem(); + + version = version.toLowerCase(Locale.ENGLISH); + + ListItem list = items; + + Deque stack = new ArrayDeque<>(); + stack.push(list); + + boolean isDigit = false; + + boolean isCombination = false; + + int startIndex = 0; + + for (int i = 0; i < version.length(); i++) { + char character = version.charAt(i); + int c = character; + if (Character.isHighSurrogate(character)) { + // read the next character as a low surrogate and combine into a single int + try { + char low = version.charAt(i + 1); + char[] both = {character, low}; + c = Character.codePointAt(both, 0); + i++; + } catch (IndexOutOfBoundsException ex) { + // high surrogate without low surrogate. Not a lot we can do here except treat it as a regular + // character + } + } + + if (c == '.') { + if (i == startIndex) { + list.add(IntItem.ZERO); + } else { + list.add(parseItem(isCombination, isDigit, version.substring(startIndex, i))); + } + isCombination = false; + startIndex = i + 1; + } else if (c == '-') { + if (i == startIndex) { + list.add(IntItem.ZERO); + } else { + // X-1 is going to be treated as X1 + if (!isDigit && i != version.length() - 1) { + char c1 = version.charAt(i + 1); + if (Character.isDigit(c1)) { + isCombination = true; + continue; + } + } + list.add(parseItem(isCombination, isDigit, version.substring(startIndex, i))); + } + startIndex = i + 1; + + if (!list.isEmpty()) { + list.add(list = new ListItem()); + stack.push(list); + } + isCombination = false; + } else if (c >= '0' && c <= '9') { // Check for ASCII digits only + if (!isDigit && i > startIndex) { + // X1 + isCombination = true; + + if (!list.isEmpty()) { + list.add(list = new ListItem()); + stack.push(list); + } + } + + isDigit = true; + } else { + if (isDigit && i > startIndex) { + list.add(parseItem(isCombination, true, version.substring(startIndex, i))); + startIndex = i; + + list.add(list = new ListItem()); + stack.push(list); + isCombination = false; + } + + isDigit = false; + } + } + + if (version.length() > startIndex) { + // 1.0.0.X1 < 1.0.0-X2 + // treat .X as -X for any string qualifier X + if (!isDigit && !list.isEmpty()) { + list.add(list = new ListItem()); + stack.push(list); + } + + list.add(parseItem(isCombination, isDigit, version.substring(startIndex))); + } + + while (!stack.isEmpty()) { + list = (ListItem) stack.pop(); + list.normalize(); + } + } + + private static Item parseItem(boolean isDigit, String buf) { + return parseItem(false, isDigit, buf); + } + + private static Item parseItem(boolean isCombination, boolean isDigit, String buf) { + if (isCombination) { + return new CombinationItem(buf.replace("-", "")); + } else if (isDigit) { + buf = stripLeadingZeroes(buf); + if (buf.length() <= MAX_INTITEM_LENGTH) { + // lower than 2^31 + return new IntItem(buf); + } else if (buf.length() <= MAX_LONGITEM_LENGTH) { + // lower than 2^63 + return new LongItem(buf); + } + return new BigIntegerItem(buf); + } + return new StringItem(buf, false); + } + + private static String stripLeadingZeroes(String buf) { + if (buf == null || buf.isEmpty()) { + return "0"; + } + for (int i = 0; i < buf.length(); ++i) { + char c = buf.charAt(i); + if (c != '0') { + return buf.substring(i); + } + } + return buf; + } + + @Override + public int compareTo(ComparableVersion o) { + return items.compareTo(o.items); + } + + @Override + public String toString() { + return value; + } + + public String getCanonical() { + if (canonical == null) { + canonical = items.toString(); + } + return canonical; + } + + @Override + public boolean equals(Object o) { + return o instanceof ComparableVersion comparableVersion && items.equals(comparableVersion.items); + } + + @Override + public int hashCode() { + return items.hashCode(); + } + + // CHECKSTYLE_OFF: LineLength + + /** + * Main to test version parsing and comparison. + *

    + * To check how "1.2.7" compares to "1.2-SNAPSHOT", for example, you can issue + *

    java -jar ${maven.repo.local}/org/apache/maven/maven-artifact/${maven.version}/maven-artifact-${maven.version}.jar "1.2.7" "1.2-SNAPSHOT"
    + * command to command line. Result of given command will be something like this: + *
    +     * Display parameters as parsed by Maven (in canonical form) and comparison result:
    +     * 1. 1.2.7 == 1.2.7
    +     *    1.2.7 > 1.2-SNAPSHOT
    +     * 2. 1.2-SNAPSHOT == 1.2-snapshot
    +     * 
    + * + * @param args the version strings to parse and compare. You can pass arbitrary number of version strings and always + * two adjacent will be compared. + */ + // CHECKSTYLE_ON: LineLength + public static void main(String... args) { + System.out.println("Display parameters as parsed by Maven (in canonical form and as a list of tokens) and" + + " comparison result:"); + if (args.length == 0) { + return; + } + + ComparableVersion prev = null; + int i = 1; + for (String version : args) { + ComparableVersion c = new ComparableVersion(version); + + if (prev != null) { + int compare = prev.compareTo(c); + System.out.println( + " " + prev + ' ' + ((compare == 0) ? "==" : ((compare < 0) ? "<" : ">")) + ' ' + version); + } + + System.out.println( + (i++) + ". " + version + " -> " + c.getCanonical() + "; tokens: " + c.items.toListString()); + + prev = c; + } + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/DefaultArtifactVersion.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/DefaultArtifactVersion.java new file mode 100644 index 000000000000..5d2264f24510 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/DefaultArtifactVersion.java @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +/** + * Default implementation of artifact versioning. + * + */ +public class DefaultArtifactVersion implements ArtifactVersion { + private Integer majorVersion; + + private Integer minorVersion; + + private Integer incrementalVersion; + + private Integer buildNumber; + + private String qualifier; + + private ComparableVersion comparable; + + public DefaultArtifactVersion(String version) { + parseVersion(version); + } + + @Override + public int hashCode() { + return 11 + comparable.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other instanceof ArtifactVersion artifactVersion) { + return compareTo(artifactVersion) == 0; + } + + return false; + } + + @Override + public int compareTo(ArtifactVersion otherVersion) { + if (otherVersion instanceof DefaultArtifactVersion defaultArtifactVersion) { + return this.comparable.compareTo(defaultArtifactVersion.comparable); + } else { + return compareTo(new DefaultArtifactVersion(otherVersion.toString())); + } + } + + @Override + public int getMajorVersion() { + return majorVersion != null ? majorVersion : 0; + } + + @Override + public int getMinorVersion() { + return minorVersion != null ? minorVersion : 0; + } + + @Override + public int getIncrementalVersion() { + return incrementalVersion != null ? incrementalVersion : 0; + } + + @Override + public int getBuildNumber() { + return buildNumber != null ? buildNumber : 0; + } + + @Override + public String getQualifier() { + return qualifier; + } + + @Override + public final void parseVersion(String version) { + comparable = new ComparableVersion(version); + + int index = version.indexOf('-'); + + String part1; + String part2 = null; + + if (index < 0) { + part1 = version; + } else { + part1 = version.substring(0, index); + part2 = version.substring(index + 1); + } + + if (part2 != null) { + if (part2.length() == 1 || !part2.startsWith("0")) { + buildNumber = tryParseInt(part2); + if (buildNumber == null) { + qualifier = part2; + } + } else { + qualifier = part2; + } + } + + if ((!part1.contains(".")) && !part1.startsWith("0")) { + majorVersion = tryParseInt(part1); + if (majorVersion == null) { + // qualifier is the whole version, including "-" + qualifier = version; + buildNumber = null; + } + } else { + boolean fallback = false; + + String[] tok = part1.split("\\."); + int idx = 0; + if (idx < tok.length) { + majorVersion = getNextIntegerToken(tok[idx++]); + if (majorVersion == null) { + fallback = true; + } + } else { + fallback = true; + } + if (idx < tok.length) { + minorVersion = getNextIntegerToken(tok[idx++]); + if (minorVersion == null) { + fallback = true; + } + } + if (idx < tok.length) { + incrementalVersion = getNextIntegerToken(tok[idx++]); + if (incrementalVersion == null) { + fallback = true; + } + } + if (idx < tok.length) { + qualifier = tok[idx]; + fallback = isDigits(qualifier); + } + + // string tokenizer won't detect these and ignores them + if (part1.contains("..") || part1.startsWith(".") || part1.endsWith(".")) { + fallback = true; + } + + if (fallback) { + // qualifier is the whole version, including "-" + qualifier = version; + majorVersion = null; + minorVersion = null; + incrementalVersion = null; + buildNumber = null; + } + } + } + + private static boolean isDigits(String cs) { + if (cs == null || cs.isEmpty()) { + return false; + } + final int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (!Character.isDigit(cs.charAt(i))) { + return false; + } + } + return true; + } + + private static Integer getNextIntegerToken(String s) { + if ((s.length() > 1) && s.startsWith("0")) { + return null; + } + return tryParseInt(s); + } + + private static Integer tryParseInt(String s) { + // for performance, check digits instead of relying later on catching NumberFormatException + if (!isDigits(s)) { + return null; + } + + try { + long longValue = Long.parseLong(s); + if (longValue > Integer.MAX_VALUE) { + return null; + } + return (int) longValue; + } catch (NumberFormatException e) { + // should never happen since checked isDigits(s) before + return null; + } + } + + @Override + public String toString() { + return comparable.toString(); + } +} diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java similarity index 75% rename from maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java rename to compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java index adc48b93d511..abc403893f3e 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.versioning; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,17 +16,14 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.versioning; /** * Occurs when a version is invalid. * - * @author Brett Porter */ -public class InvalidVersionSpecificationException - extends Exception -{ - public InvalidVersionSpecificationException( String message ) - { - super( message ); +public class InvalidVersionSpecificationException extends Exception { + public InvalidVersionSpecificationException(String message) { + super(message); } } diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/OverConstrainedVersionException.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/OverConstrainedVersionException.java new file mode 100644 index 000000000000..9a0759224d78 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/OverConstrainedVersionException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + * Occurs when ranges exclude each other and no valid value remains. + * + */ +public class OverConstrainedVersionException extends ArtifactResolutionException { + public OverConstrainedVersionException(String msg, Artifact artifact) { + super(msg, artifact); + } + + public OverConstrainedVersionException(String msg, Artifact artifact, List remoteRepositories) { + super(msg, artifact, remoteRepositories); + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/Restriction.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/Restriction.java new file mode 100644 index 000000000000..8498ec86f5a8 --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/Restriction.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +/** + * Describes a restriction in versioning. + * + */ +public class Restriction { + private final ArtifactVersion lowerBound; + + private final boolean lowerBoundInclusive; + + private final ArtifactVersion upperBound; + + private final boolean upperBoundInclusive; + + public static final Restriction EVERYTHING = new Restriction(null, false, null, false); + + public Restriction( + ArtifactVersion lowerBound, + boolean lowerBoundInclusive, + ArtifactVersion upperBound, + boolean upperBoundInclusive) { + this.lowerBound = lowerBound; + this.lowerBoundInclusive = lowerBoundInclusive; + this.upperBound = upperBound; + this.upperBoundInclusive = upperBoundInclusive; + } + + public ArtifactVersion getLowerBound() { + return lowerBound; + } + + public boolean isLowerBoundInclusive() { + return lowerBoundInclusive; + } + + public ArtifactVersion getUpperBound() { + return upperBound; + } + + public boolean isUpperBoundInclusive() { + return upperBoundInclusive; + } + + public boolean containsVersion(ArtifactVersion version) { + if (lowerBound != null) { + int comparison = lowerBound.compareTo(version); + + if ((comparison == 0) && !lowerBoundInclusive) { + return false; + } + if (comparison > 0) { + return false; + } + } + if (upperBound != null) { + int comparison = upperBound.compareTo(version); + + if ((comparison == 0) && !upperBoundInclusive) { + return false; + } + return comparison >= 0; + } + + return true; + } + + @Override + public int hashCode() { + int result = 13; + + if (lowerBound == null) { + result += 1; + } else { + result += lowerBound.hashCode(); + } + + result *= lowerBoundInclusive ? 1 : 2; + + if (upperBound == null) { + result -= 3; + } else { + result -= upperBound.hashCode(); + } + + result *= upperBoundInclusive ? 2 : 3; + + return result; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other instanceof Restriction restriction) { + if (lowerBound != null) { + if (!lowerBound.equals(restriction.lowerBound)) { + return false; + } + } else if (restriction.lowerBound != null) { + return false; + } + + if (lowerBoundInclusive != restriction.lowerBoundInclusive) { + return false; + } + + if (upperBound != null) { + if (!upperBound.equals(restriction.upperBound)) { + return false; + } + } else if (restriction.upperBound != null) { + return false; + } + + return upperBoundInclusive == restriction.upperBoundInclusive; + } else { + return false; + } + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + + buf.append(isLowerBoundInclusive() ? '[' : '('); + if (getLowerBound() != null) { + buf.append(getLowerBound().toString()); + } + buf.append(','); + if (getUpperBound() != null) { + buf.append(getUpperBound().toString()); + } + buf.append(isUpperBoundInclusive() ? ']' : ')'); + + return buf.toString(); + } +} diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java new file mode 100644 index 000000000000..152e8365c60a --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java @@ -0,0 +1,489 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; + +/** + * Construct a version range from a specification. + * + */ +public class VersionRange { + private static final Map CACHE_SPEC = Collections.synchronizedMap(new WeakHashMap<>()); + + private static final Map CACHE_VERSION = Collections.synchronizedMap(new WeakHashMap<>()); + + private final ArtifactVersion recommendedVersion; + + private final List restrictions; + + private VersionRange(ArtifactVersion recommendedVersion, List restrictions) { + this.recommendedVersion = recommendedVersion; + this.restrictions = restrictions; + } + + public ArtifactVersion getRecommendedVersion() { + return recommendedVersion; + } + + public List getRestrictions() { + return restrictions; + } + + /** + * @deprecated VersionRange is immutable, cloning is not useful and even more an issue against the cache + * @return a clone + */ + @Deprecated + public VersionRange cloneOf() { + List copiedRestrictions = null; + + if (restrictions != null) { + copiedRestrictions = new ArrayList<>(); + + if (!restrictions.isEmpty()) { + copiedRestrictions.addAll(restrictions); + } + } + + return new VersionRange(recommendedVersion, copiedRestrictions); + } + + /** + *

    + * Create a version range from a string representation + *

    + * Some spec examples are: + *
      + *
    • 1.0 Version 1.0 as a recommended version
    • + *
    • [1.0] Version 1.0 explicitly only
    • + *
    • [1.0,2.0) Versions 1.0 (included) to 2.0 (not included)
    • + *
    • [1.0,2.0] Versions 1.0 to 2.0 (both included)
    • + *
    • [1.5,) Versions 1.5 and higher
    • + *
    • (,1.0],[1.2,) Versions up to 1.0 (included) and 1.2 or higher
    • + *
    + * + * @param spec string representation of a version or version range + * @return a new {@link VersionRange} object that represents the spec + * @throws InvalidVersionSpecificationException if invalid version specification + */ + public static VersionRange createFromVersionSpec(String spec) throws InvalidVersionSpecificationException { + if (spec == null) { + return null; + } + + VersionRange cached = CACHE_SPEC.get(spec); + if (cached != null) { + return cached; + } + + List restrictions = new ArrayList<>(); + String process = spec; + ArtifactVersion version = null; + ArtifactVersion upperBound = null; + ArtifactVersion lowerBound = null; + + while (process.startsWith("[") || process.startsWith("(")) { + int index1 = process.indexOf(')'); + int index2 = process.indexOf(']'); + + int index = index2; + if (index2 < 0 || index1 < index2) { + if (index1 >= 0) { + index = index1; + } + } + + if (index < 0) { + throw new InvalidVersionSpecificationException("Unbounded range: " + spec); + } + + Restriction restriction = parseRestriction(process.substring(0, index + 1)); + if (lowerBound == null) { + lowerBound = restriction.getLowerBound(); + } + if (upperBound != null) { + if (restriction.getLowerBound() == null + || restriction.getLowerBound().compareTo(upperBound) < 0) { + throw new InvalidVersionSpecificationException("Ranges overlap: " + spec); + } + } + restrictions.add(restriction); + upperBound = restriction.getUpperBound(); + + process = process.substring(index + 1).trim(); + + if (process.startsWith(",")) { + process = process.substring(1).trim(); + } + } + + if (!process.isEmpty()) { + if (!restrictions.isEmpty()) { + throw new InvalidVersionSpecificationException( + "Only fully-qualified sets allowed in multiple set scenario: " + spec); + } else { + version = new DefaultArtifactVersion(process); + restrictions.add(Restriction.EVERYTHING); + } + } + + cached = new VersionRange(version, restrictions); + CACHE_SPEC.put(spec, cached); + return cached; + } + + private static Restriction parseRestriction(String spec) throws InvalidVersionSpecificationException { + boolean lowerBoundInclusive = spec.startsWith("["); + boolean upperBoundInclusive = spec.endsWith("]"); + + String process = spec.substring(1, spec.length() - 1).trim(); + + Restriction restriction; + + int index = process.indexOf(','); + + if (index < 0) { + if (!lowerBoundInclusive || !upperBoundInclusive) { + throw new InvalidVersionSpecificationException("Single version must be surrounded by []: " + spec); + } + + ArtifactVersion version = new DefaultArtifactVersion(process); + + restriction = new Restriction(version, lowerBoundInclusive, version, upperBoundInclusive); + } else { + String lowerBound = process.substring(0, index).trim(); + String upperBound = process.substring(index + 1).trim(); + + ArtifactVersion lowerVersion = null; + if (!lowerBound.isEmpty()) { + lowerVersion = new DefaultArtifactVersion(lowerBound); + } + ArtifactVersion upperVersion = null; + if (!upperBound.isEmpty()) { + upperVersion = new DefaultArtifactVersion(upperBound); + } + + if (upperVersion != null && lowerVersion != null) { + int result = upperVersion.compareTo(lowerVersion); + if (result < 0 || (result == 0 && (!lowerBoundInclusive || !upperBoundInclusive))) { + throw new InvalidVersionSpecificationException("Range defies version ordering: " + spec); + } + } + + restriction = new Restriction(lowerVersion, lowerBoundInclusive, upperVersion, upperBoundInclusive); + } + + return restriction; + } + + public static VersionRange createFromVersion(String version) { + if (DefaultArtifact.empty(version)) { + return null; + } + VersionRange cached = CACHE_VERSION.get(version); + if (cached == null) { + List restrictions = Collections.emptyList(); + cached = new VersionRange(new DefaultArtifactVersion(version), restrictions); + CACHE_VERSION.put(version, cached); + } + return cached; + } + + /** + * Creates and returns a new VersionRange that is a restriction of this + * version range and the specified version range. + *

    + * Note: Precedence is given to the recommended version from this version range over the + * recommended version from the specified version range. + *

    + * + * @param restriction the VersionRange that will be used to restrict this version + * range. + * @return the VersionRange that is a restriction of this version range and the + * specified version range. + *

    + * The restrictions of the returned version range will be an intersection of the restrictions + * of this version range and the specified version range if both version ranges have + * restrictions. Otherwise, the restrictions on the returned range will be empty. + *

    + *

    + * The recommended version of the returned version range will be the recommended version of + * this version range, provided that ranges falls within the intersected restrictions. If + * the restrictions are empty, this version range's recommended version is used if it is not + * null. If it is null, the specified version range's recommended + * version is used (provided it is non-null). If no recommended version can be + * obtained, the returned version range's recommended version is set to null. + *

    + * @throws NullPointerException if the specified VersionRange is + * null. + */ + public VersionRange restrict(VersionRange restriction) { + List r1 = this.restrictions; + List r2 = restriction.restrictions; + List restrictions; + + if (r1.isEmpty() || r2.isEmpty()) { + restrictions = Collections.emptyList(); + } else { + restrictions = Collections.unmodifiableList(intersection(r1, r2)); + } + + ArtifactVersion version = null; + if (!restrictions.isEmpty()) { + for (Restriction r : restrictions) { + if (recommendedVersion != null && r.containsVersion(recommendedVersion)) { + // if we find the original, use that + version = recommendedVersion; + break; + } else if (version == null + && restriction.getRecommendedVersion() != null + && r.containsVersion(restriction.getRecommendedVersion())) { + // use this if we can, but prefer the original if possible + version = restriction.getRecommendedVersion(); + } + } + } + // Either the original or the specified version ranges have no restrictions + else if (recommendedVersion != null) { + // Use the original recommended version since it exists + version = recommendedVersion; + } else if (restriction.recommendedVersion != null) { + // Use the recommended version from the specified VersionRange since there is no + // original recommended version + version = restriction.recommendedVersion; + } + /* TODO should throw this immediately, but need artifact + else + { + throw new OverConstrainedVersionException( "Restricting incompatible version ranges" ); + } + */ + + return new VersionRange(version, restrictions); + } + + private List intersection(List r1, List r2) { + List restrictions = new ArrayList<>(r1.size() + r2.size()); + Iterator i1 = r1.iterator(); + Iterator i2 = r2.iterator(); + Restriction res1 = i1.next(); + Restriction res2 = i2.next(); + + boolean done = false; + while (!done) { + if (res1.getLowerBound() == null + || res2.getUpperBound() == null + || res1.getLowerBound().compareTo(res2.getUpperBound()) <= 0) { + if (res1.getUpperBound() == null + || res2.getLowerBound() == null + || res1.getUpperBound().compareTo(res2.getLowerBound()) >= 0) { + ArtifactVersion lower; + ArtifactVersion upper; + boolean lowerInclusive; + boolean upperInclusive; + + // overlaps + if (res1.getLowerBound() == null) { + lower = res2.getLowerBound(); + lowerInclusive = res2.isLowerBoundInclusive(); + } else if (res2.getLowerBound() == null) { + lower = res1.getLowerBound(); + lowerInclusive = res1.isLowerBoundInclusive(); + } else { + int comparison = res1.getLowerBound().compareTo(res2.getLowerBound()); + if (comparison < 0) { + lower = res2.getLowerBound(); + lowerInclusive = res2.isLowerBoundInclusive(); + } else if (comparison == 0) { + lower = res1.getLowerBound(); + lowerInclusive = res1.isLowerBoundInclusive() && res2.isLowerBoundInclusive(); + } else { + lower = res1.getLowerBound(); + lowerInclusive = res1.isLowerBoundInclusive(); + } + } + + if (res1.getUpperBound() == null) { + upper = res2.getUpperBound(); + upperInclusive = res2.isUpperBoundInclusive(); + } else if (res2.getUpperBound() == null) { + upper = res1.getUpperBound(); + upperInclusive = res1.isUpperBoundInclusive(); + } else { + int comparison = res1.getUpperBound().compareTo(res2.getUpperBound()); + if (comparison < 0) { + upper = res1.getUpperBound(); + upperInclusive = res1.isUpperBoundInclusive(); + } else if (comparison == 0) { + upper = res1.getUpperBound(); + upperInclusive = res1.isUpperBoundInclusive() && res2.isUpperBoundInclusive(); + } else { + upper = res2.getUpperBound(); + upperInclusive = res2.isUpperBoundInclusive(); + } + } + + // don't add if they are equal and one is not inclusive + if (lower == null || upper == null || lower.compareTo(upper) != 0) { + restrictions.add(new Restriction(lower, lowerInclusive, upper, upperInclusive)); + } else if (lowerInclusive && upperInclusive) { + restrictions.add(new Restriction(lower, lowerInclusive, upper, upperInclusive)); + } + + //noinspection ObjectEquality + if (upper == res2.getUpperBound()) { + // advance res2 + if (i2.hasNext()) { + res2 = i2.next(); + } else { + done = true; + } + } else { + // advance res1 + if (i1.hasNext()) { + res1 = i1.next(); + } else { + done = true; + } + } + } else { + // move on to next in r1 + if (i1.hasNext()) { + res1 = i1.next(); + } else { + done = true; + } + } + } else { + // move on to next in r2 + if (i2.hasNext()) { + res2 = i2.next(); + } else { + done = true; + } + } + } + + return restrictions; + } + + public ArtifactVersion getSelectedVersion(Artifact artifact) throws OverConstrainedVersionException { + ArtifactVersion version; + if (recommendedVersion != null) { + version = recommendedVersion; + } else { + if (restrictions.isEmpty()) { + throw new OverConstrainedVersionException("The artifact has no valid ranges", artifact); + } + + version = null; + } + return version; + } + + public boolean isSelectedVersionKnown(Artifact artifact) throws OverConstrainedVersionException { + boolean value = false; + if (recommendedVersion != null) { + value = true; + } else { + if (restrictions.isEmpty()) { + throw new OverConstrainedVersionException("The artifact has no valid ranges", artifact); + } + } + return value; + } + + @Override + public String toString() { + if (recommendedVersion != null) { + return recommendedVersion.toString(); + } else { + StringBuilder buf = new StringBuilder(); + for (Iterator i = restrictions.iterator(); i.hasNext(); ) { + Restriction r = i.next(); + + buf.append(r.toString()); + + if (i.hasNext()) { + buf.append(','); + } + } + return buf.toString(); + } + } + + public ArtifactVersion matchVersion(List versions) { + // TODO could be more efficient by sorting the list and then moving along the restrictions in order? + + ArtifactVersion matched = null; + for (ArtifactVersion version : versions) { + if (containsVersion(version)) { + // valid - check if it is greater than the currently matched version + if (matched == null || version.compareTo(matched) > 0) { + matched = version; + } + } + } + return matched; + } + + public boolean containsVersion(ArtifactVersion version) { + for (Restriction restriction : restrictions) { + if (restriction.containsVersion(version)) { + return true; + } + } + return false; + } + + public boolean hasRestrictions() { + return !restrictions.isEmpty() && recommendedVersion == null; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof VersionRange other) { + return Objects.equals(recommendedVersion, other.recommendedVersion) + && Objects.equals(restrictions, other.restrictions); + } else { + return false; + } + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + (recommendedVersion == null ? 0 : recommendedVersion.hashCode()); + hash = 31 * hash + (restrictions == null ? 0 : restrictions.hashCode()); + return hash; + } +} diff --git a/maven-artifact/src/main/java/org/apache/maven/repository/Proxy.java b/compat/maven-artifact/src/main/java/org/apache/maven/repository/Proxy.java similarity index 79% rename from maven-artifact/src/main/java/org/apache/maven/repository/Proxy.java rename to compat/maven-artifact/src/main/java/org/apache/maven/repository/Proxy.java index 960d7beb7040..aa27e6286db0 100644 --- a/maven-artifact/src/main/java/org/apache/maven/repository/Proxy.java +++ b/compat/maven-artifact/src/main/java/org/apache/maven/repository/Proxy.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository; /** * Proxy */ -public class Proxy -{ +public class Proxy { public static final String PROXY_SOCKS5 = "SOCKS_5"; public static final String PROXY_SOCKS4 = "SOCKS4"; @@ -75,8 +73,7 @@ public class Proxy * * @return proxy server host name */ - public String getHost() - { + public String getHost() { return host; } @@ -85,28 +82,25 @@ public String getHost() * * @param host proxy server host name */ - public void setHost( String host ) - { + public void setHost(String host) { this.host = host; } /** - * Get user's password used to login to proxy server. + * Get user's password used to log in to proxy server. * * @return user's password at proxy host */ - public String getPassword() - { + public String getPassword() { return password; } /** * Set the user's password for the proxy server. * - * @param password password to use to login to a proxy server + * @param password password to use to log in to a proxy server */ - public void setPassword( String password ) - { + public void setPassword(String password) { this.password = password; } @@ -115,8 +109,7 @@ public void setPassword( String password ) * * @return proxy server port */ - public int getPort() - { + public int getPort() { return port; } @@ -125,8 +118,7 @@ public int getPort() * * @param port proxy server port */ - public void setPort( int port ) - { + public void setPort(int port) { this.port = port; } @@ -135,8 +127,7 @@ public void setPort( int port ) * * @return username for the proxy server */ - public String getUserName() - { + public String getUserName() { return userName; } @@ -145,8 +136,7 @@ public String getUserName() * * @param userName username for the proxy server */ - public void setUserName( String userName ) - { + public void setUserName(String userName) { this.userName = userName; } @@ -155,46 +145,38 @@ public void setUserName( String userName ) * * @return the protocol of the proxy server */ - public String getProtocol() - { + public String getProtocol() { return protocol; } /** * @param protocol the protocol of the proxy server like SOCKSv4 */ - public void setProtocol( String protocol ) - { + public void setProtocol(String protocol) { this.protocol = protocol; } - public String getNonProxyHosts() - { + public String getNonProxyHosts() { return nonProxyHosts; } - public void setNonProxyHosts( String nonProxyHosts ) - { + public void setNonProxyHosts(String nonProxyHosts) { this.nonProxyHosts = nonProxyHosts; } - public String getNtlmHost() - { + public String getNtlmHost() { return ntlmHost; } - public void setNtlmHost( String ntlmHost ) - { + public void setNtlmHost(String ntlmHost) { this.ntlmHost = ntlmHost; } - public void setNtlmDomain( String ntlmDomain ) - { + public void setNtlmDomain(String ntlmDomain) { this.ntlmDomain = ntlmDomain; } - public String getNtlmDomain() - { + public String getNtlmDomain() { return ntlmDomain; } } diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadata.java b/compat/maven-artifact/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadata.java new file mode 100644 index 000000000000..5a9ec7eeeb5e --- /dev/null +++ b/compat/maven-artifact/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadata.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataStoreException; + +/** + * Contains metadata about an artifact, and methods to retrieve/store it from an artifact repository. + * TODO merge with artifactmetadatasource + * TODO retrieval exception not appropriate for store + * + */ +public interface ArtifactMetadata { + /** + * Whether this metadata should be stored alongside the artifact. + * + * @return whether this metadata should be stored alongside the artifact + */ + boolean storedInArtifactVersionDirectory(); + + /** + * Whether this metadata should be stored alongside the group. + * + * @return whether this metadata should be stored alongside the group + */ + boolean storedInGroupDirectory(); + + String getGroupId(); + + String getArtifactId(); + + String getBaseVersion(); + + Object getKey(); + + /** + * Get the filename of this metadata on the local repository. + * + * @param repository the remote repository it came from + * @return the filename + */ + String getLocalFilename(ArtifactRepository repository); + + /** + * Get the filename of this metadata on the remote repository. + * + * @return the filename + */ + String getRemoteFilename(); + + /** + * Merge a new metadata set into this piece of metadata. + * TODO this should only be needed on the repository metadata {@link org.apache.maven.artifact.metadata.ArtifactMetadata} + * + * @param metadata the new metadata + */ + void merge(ArtifactMetadata metadata); + + /** + * Store the metadata in the local repository. + * TODO this should only be needed on the repository metadata {@link org.apache.maven.artifact.metadata.ArtifactMetadata} + * + * @param localRepository the local repository + * @param remoteRepository the remote repository it came from + * @throws RepositoryMetadataStoreException in case of issue + */ + void storeInLocalRepository(ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataStoreException; + + String extendedToString(); +} diff --git a/maven-artifact/src/site/apt/index.apt b/compat/maven-artifact/src/site/apt/index.apt similarity index 100% rename from maven-artifact/src/site/apt/index.apt rename to compat/maven-artifact/src/site/apt/index.apt diff --git a/compat/maven-artifact/src/site/site.xml b/compat/maven-artifact/src/site/site.xml new file mode 100644 index 000000000000..4ee3b709cfc4 --- /dev/null +++ b/compat/maven-artifact/src/site/site.xml @@ -0,0 +1,35 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/ArtifactUtilsTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/ArtifactUtilsTest.java new file mode 100644 index 000000000000..740bdee4100b --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/ArtifactUtilsTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.maven.artifact.versioning.VersionRange; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests {@link ArtifactUtils}. + * + */ +class ArtifactUtilsTest { + + private Artifact newArtifact(String aid) { + return new DefaultArtifact("group", aid, VersionRange.createFromVersion("1.0"), "test", "jar", "tests", null); + } + + @Test + void testIsSnapshot() { + assertFalse(ArtifactUtils.isSnapshot(null)); + assertFalse(ArtifactUtils.isSnapshot("")); + assertFalse(ArtifactUtils.isSnapshot("1.2.3")); + assertTrue(ArtifactUtils.isSnapshot("1.2.3-SNAPSHOT")); + assertTrue(ArtifactUtils.isSnapshot("1.2.3-snapshot")); + assertTrue(ArtifactUtils.isSnapshot("1.2.3-20090413.094722-2")); + assertFalse(ArtifactUtils.isSnapshot("1.2.3-20090413X094722-2")); + } + + @Test + void testToSnapshotVersion() { + assertEquals("1.2.3", ArtifactUtils.toSnapshotVersion("1.2.3")); + assertEquals("1.2.3-SNAPSHOT", ArtifactUtils.toSnapshotVersion("1.2.3-SNAPSHOT")); + assertEquals("1.2.3-SNAPSHOT", ArtifactUtils.toSnapshotVersion("1.2.3-20090413.094722-2")); + assertEquals("1.2.3-20090413X094722-2", ArtifactUtils.toSnapshotVersion("1.2.3-20090413X094722-2")); + } + + /** + * Tests that the ordering of the map resembles the ordering of the input collection of artifacts. + */ + @Test + void testArtifactMapByVersionlessIdOrdering() throws Exception { + List list = new ArrayList<>(); + list.add(newArtifact("b")); + list.add(newArtifact("a")); + list.add(newArtifact("c")); + list.add(newArtifact("e")); + list.add(newArtifact("d")); + + Map map = ArtifactUtils.artifactMapByVersionlessId(list); + assertNotNull(map); + assertEquals(list, new ArrayList<>(map.values())); + } +} diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/DefaultArtifactTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/DefaultArtifactTest.java new file mode 100644 index 000000000000..74fcd5fb93a8 --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/DefaultArtifactTest.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import java.util.stream.Stream; + +import org.apache.maven.artifact.handler.ArtifactHandlerMock; +import org.apache.maven.artifact.versioning.VersionRange; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DefaultArtifactTest { + + private DefaultArtifact artifact; + + private DefaultArtifact snapshotArtifact; + + private String groupId = "groupid", + artifactId = "artifactId", + version = "1.0", + scope = "artifactScope", + type = "type", + classifier = "classifier"; + + private String snapshotSpecVersion = "1.0-SNAPSHOT"; + private String snapshotResolvedVersion = "1.0-20070606.010101-1"; + + private VersionRange versionRange; + private VersionRange snapshotVersionRange; + + private ArtifactHandlerMock artifactHandler; + + @BeforeEach + void setUp() throws Exception { + artifactHandler = new ArtifactHandlerMock(); + versionRange = VersionRange.createFromVersion(version); + artifact = new DefaultArtifact(groupId, artifactId, versionRange, scope, type, classifier, artifactHandler); + + snapshotVersionRange = VersionRange.createFromVersion(snapshotResolvedVersion); + snapshotArtifact = new DefaultArtifact( + groupId, artifactId, snapshotVersionRange, scope, type, classifier, artifactHandler); + } + + @Test + void testGetVersionReturnsResolvedVersionOnSnapshot() { + assertEquals(snapshotResolvedVersion, snapshotArtifact.getVersion()); + + // this is FOUL! + // snapshotArtifact.isSnapshot(); + + assertEquals(snapshotSpecVersion, snapshotArtifact.getBaseVersion()); + } + + @Test + void testGetDependencyConflictId() { + assertEquals(groupId + ":" + artifactId + ":" + type + ":" + classifier, artifact.getDependencyConflictId()); + } + + @Test + void testGetDependencyConflictIdNullGroupId() { + artifact.setGroupId(null); + assertEquals(null + ":" + artifactId + ":" + type + ":" + classifier, artifact.getDependencyConflictId()); + } + + @Test + void testGetDependencyConflictIdNullClassifier() { + artifact = new DefaultArtifact(groupId, artifactId, versionRange, scope, type, null, artifactHandler); + assertEquals(groupId + ":" + artifactId + ":" + type, artifact.getDependencyConflictId()); + } + + @Test + void testGetDependencyConflictIdNullScope() { + artifact.setScope(null); + assertEquals(groupId + ":" + artifactId + ":" + type + ":" + classifier, artifact.getDependencyConflictId()); + } + + @Test + void testToString() { + assertEquals( + groupId + ":" + artifactId + ":" + type + ":" + classifier + ":" + version + ":" + scope, + artifact.toString()); + } + + @Test + void testToStringNullGroupId() { + artifact.setGroupId(null); + assertEquals(artifactId + ":" + type + ":" + classifier + ":" + version + ":" + scope, artifact.toString()); + } + + @Test + void testToStringNullClassifier() { + artifact = new DefaultArtifact(groupId, artifactId, versionRange, scope, type, null, artifactHandler); + assertEquals(groupId + ":" + artifactId + ":" + type + ":" + version + ":" + scope, artifact.toString()); + } + + @Test + void testToStringNullScope() { + artifact.setScope(null); + assertEquals(groupId + ":" + artifactId + ":" + type + ":" + classifier + ":" + version, artifact.toString()); + } + + @Test + void testComparisonByVersion() { + Artifact artifact1 = new DefaultArtifact( + groupId, artifactId, VersionRange.createFromVersion("5.0"), scope, type, classifier, artifactHandler); + Artifact artifact2 = new DefaultArtifact( + groupId, artifactId, VersionRange.createFromVersion("12.0"), scope, type, classifier, artifactHandler); + + assertTrue(artifact1.compareTo(artifact2) < 0); + assertTrue(artifact2.compareTo(artifact1) > 0); + + Artifact artifact = new DefaultArtifact( + groupId, artifactId, VersionRange.createFromVersion("5.0"), scope, type, classifier, artifactHandler); + assertTrue(artifact.compareTo(artifact1) == 0); + assertTrue(artifact1.compareTo(artifact) == 0); + } + + @Test + void testNonResolvedVersionRangeConsistentlyYieldsNullVersions() throws Exception { + VersionRange vr = VersionRange.createFromVersionSpec("[1.0,2.0)"); + artifact = new DefaultArtifact(groupId, artifactId, vr, scope, type, null, artifactHandler); + assertNull(artifact.getVersion()); + assertNull(artifact.getBaseVersion()); + } + + @Test + void testMNG7780() throws Exception { + VersionRange vr = VersionRange.createFromVersionSpec("[1.0,2.0)"); + artifact = new DefaultArtifact(groupId, artifactId, vr, scope, type, null, artifactHandler); + assertNull(artifact.getVersion()); + assertNull(artifact.getBaseVersion()); + + DefaultArtifact nullVersionArtifact = + new DefaultArtifact(groupId, artifactId, vr, scope, type, null, artifactHandler); + assertEquals(artifact, nullVersionArtifact); + } + + @ParameterizedTest + @MethodSource("invalidMavenCoordinates") + void testIllegalCoordinatesInConstructor(String groupId, String artifactId, String version) { + assertThrows( + InvalidArtifactRTException.class, + () -> new DefaultArtifact( + groupId, artifactId, version, scope, type, classifier, artifactHandler, false)); + if (version == null) { + assertThrows( + InvalidArtifactRTException.class, + () -> new DefaultArtifact( + groupId, artifactId, (VersionRange) null, scope, type, classifier, artifactHandler, false)); + } + } + + static Stream invalidMavenCoordinates() { + return Stream.of( + Arguments.of(null, null, null), + Arguments.of("", "", ""), + Arguments.of(null, "artifactId", "1.0"), + Arguments.of("", "artifactId", "1.0"), + Arguments.of("groupId", null, "1.0"), + Arguments.of("groupId", "", "1.0"), + Arguments.of("groupId", "artifactId", null), + Arguments.of("groupId", "artifactId", "")); + } +} diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java new file mode 100644 index 000000000000..f70db5bcbd32 --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.handler; + +public class ArtifactHandlerMock implements ArtifactHandler { + + private String extension, directory, classifier, packaging, language; + private boolean includesDependencies, addedToClasspath; + + public void setExtension(String extension) { + this.extension = extension; + } + + @Override + public String getExtension() { + return extension; + } + + public void setDirectory(String directory) { + this.directory = directory; + } + + @Override + public String getDirectory() { + return directory; + } + + public void setClassifier(String classifier) { + this.classifier = classifier; + } + + @Override + public String getClassifier() { + return classifier; + } + + public void setPackaging(String packaging) { + this.packaging = packaging; + } + + @Override + public String getPackaging() { + return packaging; + } + + public void setIncludesDependencies(boolean includesDependencies) { + this.includesDependencies = includesDependencies; + } + + @Override + public boolean isIncludesDependencies() { + return includesDependencies; + } + + public void setLanguage(String language) { + this.language = language; + } + + @Override + public String getLanguage() { + return language; + } + + @Deprecated + public void setAddedToClasspath(boolean addedToClasspath) { + this.addedToClasspath = addedToClasspath; + } + + @Override + @Deprecated + public boolean isAddedToClasspath() { + return addedToClasspath; + } +} diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionIT.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionIT.java new file mode 100644 index 000000000000..a78591dafd76 --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionIT.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ComparableVersionIT { + + @Test + void test() throws Exception { + Files.walkFileTree(Paths.get("target"), new SimpleFileVisitor() { + Pattern mavenArtifactJar = Pattern.compile("maven-artifact-[\\d.]+(-SNAPSHOT)?\\.jar"); + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String filename = file.getFileName().toString(); + if (mavenArtifactJar.matcher(filename).matches()) { + Process p = Runtime.getRuntime().exec(new String[] { + Paths.get(System.getProperty("java.home"), "bin/java").toString(), + "-jar", + file.toAbsolutePath().toString(), + "5.32", + "5.27" + }); + + try { + assertEquals(0, p.waitFor(), "Unexpected exit code"); + } catch (InterruptedException e) { + throw new InterruptedIOException(e.toString()); + } + return FileVisitResult.TERMINATE; + } else { + return FileVisitResult.CONTINUE; + } + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (Paths.get("target").equals(dir)) { + return FileVisitResult.CONTINUE; + } else { + return FileVisitResult.SKIP_SUBTREE; + } + } + }); + } +} diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java new file mode 100644 index 000000000000..11561aa16689 --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java @@ -0,0 +1,488 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test ComparableVersion. + */ +@SuppressWarnings("unchecked") +class ComparableVersionTest { + private ComparableVersion newComparable(String version) { + ComparableVersion ret = new ComparableVersion(version); + String canonical = ret.getCanonical(); + String parsedCanonical = new ComparableVersion(canonical).getCanonical(); + + assertEquals( + canonical, + parsedCanonical, + "canonical( " + version + " ) = " + canonical + " -> canonical: " + parsedCanonical); + + return ret; + } + + private static final String[] VERSIONS_QUALIFIER = { + "1-alpha2snapshot", + "1-alpha2", + "1-alpha-123", + "1-beta-2", + "1-beta123", + "1-m2", + "1-m11", + "1-rc", + "1-cr2", + "1-rc123", + "1-SNAPSHOT", + "1", + "1-sp", + "1-sp2", + "1-sp123", + "1-abc", + "1-def", + "1-pom-1", + "1-1-snapshot", + "1-1", + "1-2", + "1-123" + }; + + private static final String[] VERSIONS_NUMBER = { + "2.0", "2.0.a", "2-1", "2.0.2", "2.0.123", "2.1.0", "2.1-a", "2.1b", "2.1-c", "2.1-1", "2.1.0.1", "2.2", + "2.123", "11.a2", "11.a11", "11.b2", "11.b11", "11.m2", "11.m11", "11", "11.a", "11b", "11c", "11m" + }; + + private void checkVersionsOrder(String[] versions) { + Comparable[] c = new Comparable[versions.length]; + for (int i = 0; i < versions.length; i++) { + c[i] = newComparable(versions[i]); + } + + for (int i = 1; i < versions.length; i++) { + Comparable low = c[i - 1]; + for (int j = i; j < versions.length; j++) { + Comparable high = c[j]; + assertTrue(low.compareTo(high) < 0, "expected " + low + " < " + high); + assertTrue(high.compareTo(low) > 0, "expected " + high + " > " + low); + } + } + } + + private void checkVersionsEqual(String v1, String v2) { + Comparable c1 = newComparable(v1); + Comparable c2 = newComparable(v2); + assertEquals(0, c1.compareTo(c2), "expected " + v1 + " == " + v2); + assertEquals(0, c2.compareTo(c1), "expected " + v2 + " == " + v1); + assertEquals(c1.hashCode(), c2.hashCode(), "expected same hashcode for " + v1 + " and " + v2); + assertEquals(c1, c2, "expected " + v1 + ".equals( " + v2 + " )"); + assertEquals(c2, c1, "expected " + v2 + ".equals( " + v1 + " )"); + } + + private void checkVersionsHaveSameOrder(String v1, String v2) { + ComparableVersion c1 = new ComparableVersion(v1); + ComparableVersion c2 = new ComparableVersion(v2); + assertEquals(0, c1.compareTo(c2), "expected " + v1 + " == " + v2); + assertEquals(0, c2.compareTo(c1), "expected " + v2 + " == " + v1); + } + + private void checkVersionsArrayEqual(String[] array) { + // compare against each other (including itself) + for (int i = 0; i < array.length; ++i) { + for (int j = i; j < array.length; ++j) { + checkVersionsEqual(array[i], array[j]); + } + } + } + + private void checkVersionsOrder(String v1, String v2) { + Comparable c1 = newComparable(v1); + Comparable c2 = newComparable(v2); + assertTrue(c1.compareTo(c2) < 0, "expected " + v1 + " < " + v2); + assertTrue(c2.compareTo(c1) > 0, "expected " + v2 + " > " + v1); + } + + @Test + void testVersionsQualifier() { + checkVersionsOrder(VERSIONS_QUALIFIER); + } + + @Test + void testVersionsNumber() { + checkVersionsOrder(VERSIONS_NUMBER); + } + + @Test + void testVersionsEqual() { + newComparable("1.0-alpha"); + checkVersionsEqual("1", "1"); + checkVersionsEqual("1", "1.0"); + checkVersionsEqual("1", "1.0.0"); + checkVersionsEqual("1.0", "1.0.0"); + checkVersionsEqual("1", "1-0"); + checkVersionsEqual("1", "1.0-0"); + checkVersionsEqual("1.0", "1.0-0"); + // no separator between number and character + checkVersionsEqual("1a", "1-a"); + checkVersionsEqual("1a", "1.0-a"); + checkVersionsEqual("1a", "1.0.0-a"); + checkVersionsEqual("1.0a", "1-a"); + checkVersionsEqual("1.0.0a", "1-a"); + checkVersionsEqual("1x", "1-x"); + checkVersionsEqual("1x", "1.0-x"); + checkVersionsEqual("1x", "1.0.0-x"); + checkVersionsEqual("1.0x", "1-x"); + checkVersionsEqual("1.0.0x", "1-x"); + checkVersionsEqual("1cr", "1rc"); + + // special "aliases" a, b and m for alpha, beta and milestone + checkVersionsEqual("1a1", "1-alpha-1"); + checkVersionsEqual("1b2", "1-beta-2"); + checkVersionsEqual("1m3", "1-milestone-3"); + + // case insensitive + checkVersionsEqual("1X", "1x"); + checkVersionsEqual("1A", "1a"); + checkVersionsEqual("1B", "1b"); + checkVersionsEqual("1M", "1m"); + checkVersionsEqual("1Cr", "1Rc"); + checkVersionsEqual("1cR", "1rC"); + checkVersionsEqual("1m3", "1Milestone3"); + checkVersionsEqual("1m3", "1MileStone3"); + checkVersionsEqual("1m3", "1MILESTONE3"); + } + + @Test + void testVersionsHaveSameOrderButAreNotEqual() { + checkVersionsHaveSameOrder("1ga", "1"); + checkVersionsHaveSameOrder("1release", "1"); + checkVersionsHaveSameOrder("1final", "1"); + checkVersionsHaveSameOrder("1Ga", "1"); + checkVersionsHaveSameOrder("1GA", "1"); + checkVersionsHaveSameOrder("1RELEASE", "1"); + checkVersionsHaveSameOrder("1release", "1"); + checkVersionsHaveSameOrder("1RELeaSE", "1"); + checkVersionsHaveSameOrder("1Final", "1"); + checkVersionsHaveSameOrder("1FinaL", "1"); + checkVersionsHaveSameOrder("1FINAL", "1"); + } + + @Test + void testVersionComparing() { + checkVersionsOrder("1", "2"); + checkVersionsOrder("1.5", "2"); + checkVersionsOrder("1", "2.5"); + checkVersionsOrder("1.0", "1.1"); + checkVersionsOrder("1.1", "1.2"); + checkVersionsOrder("1.0.0", "1.1"); + checkVersionsOrder("1.0.1", "1.1"); + checkVersionsOrder("1.1", "1.2.0"); + + checkVersionsOrder("1.0-alpha-1", "1.0"); + checkVersionsOrder("1.0-alpha-1", "1.0-alpha-2"); + checkVersionsOrder("1.0-alpha-1", "1.0-beta-1"); + + checkVersionsOrder("1.0-beta-1", "1.0-SNAPSHOT"); + checkVersionsOrder("1.0-SNAPSHOT", "1.0"); + checkVersionsOrder("1.0-alpha-1-SNAPSHOT", "1.0-alpha-1"); + + checkVersionsOrder("1.0", "1.0-1"); + checkVersionsOrder("1.0-1", "1.0-2"); + checkVersionsOrder("1.0.0", "1.0-1"); + + checkVersionsOrder("2.0-1", "2.0.1"); + checkVersionsOrder("2.0.1-klm", "2.0.1-lmn"); + checkVersionsOrder("2.0.1", "2.0.1-xyz"); + + checkVersionsOrder("2.0.1", "2.0.1-123"); + checkVersionsOrder("2.0.1-xyz", "2.0.1-123"); + } + + @Test + void testLeadingZeroes() { + checkVersionsOrder("0.7", "2"); + checkVersionsOrder("0.2", "1.0.7"); + } + + @Test + void testDigitGreaterThanNonAscii() { + ComparableVersion c1 = new ComparableVersion("1"); + ComparableVersion c2 = new ComparableVersion("é"); + assertTrue(c1.compareTo(c2) > 0, "expected " + "1" + " > " + "\uD835\uDFE4"); + assertTrue(c2.compareTo(c1) < 0, "expected " + "\uD835\uDFE4" + " < " + "1"); + } + + @Test + void testDigitGreaterThanNonBmpCharacters() { + ComparableVersion c1 = new ComparableVersion("1"); + // MATHEMATICAL SANS-SERIF DIGIT TWO + ComparableVersion c2 = new ComparableVersion("\uD835\uDFE4"); + assertTrue(c1.compareTo(c2) > 0, "expected " + "1" + " > " + "\uD835\uDFE4"); + assertTrue(c2.compareTo(c1) < 0, "expected " + "\uD835\uDFE4" + " < " + "1"); + } + + @Test + void testGetCanonical() { + // MNG-7700 + newComparable("0.x"); + newComparable("0-x"); + newComparable("0.rc"); + newComparable("0-1"); + + ComparableVersion version = new ComparableVersion("0.x"); + assertEquals("x", version.getCanonical()); + ComparableVersion version2 = new ComparableVersion("0.2"); + assertEquals("0.2", version2.getCanonical()); + } + + @Test + void testLexicographicASCIISortOrder() { // Required by Semver 1.0 + ComparableVersion lower = new ComparableVersion("1.0.0-alpha1"); + ComparableVersion upper = new ComparableVersion("1.0.0-ALPHA1"); + // Lower case is equal to upper case. This is *NOT* what Semver 1.0 + // specifies. Here we are explicitly deviating from Semver 1.0. + assertTrue(upper.compareTo(lower) == 0, "expected 1.0.0-ALPHA1 == 1.0.0-alpha1"); + assertTrue(lower.compareTo(upper) == 0, "expected 1.0.0-alpha1 == 1.0.0-ALPHA1"); + } + + @Test + void testCompareLowerCaseToUpperCaseASCII() { + ComparableVersion lower = new ComparableVersion("1.a"); + ComparableVersion upper = new ComparableVersion("1.A"); + // Lower case is equal to upper case + assertTrue(upper.compareTo(lower) == 0, "expected 1.A == 1.a"); + assertTrue(lower.compareTo(upper) == 0, "expected 1.a == 1.A"); + } + + @Test + void testCompareLowerCaseToUpperCaseNonASCII() { + ComparableVersion lower = new ComparableVersion("1.é"); + ComparableVersion upper = new ComparableVersion("1.É"); + // Lower case is equal to upper case + assertTrue(upper.compareTo(lower) == 0, "expected 1.É < 1.é"); + assertTrue(lower.compareTo(upper) == 0, "expected 1.é > 1.É"); + } + + @Test + void testCompareDigitToLetter() { + ComparableVersion seven = new ComparableVersion("7"); + ComparableVersion capitalJ = new ComparableVersion("J"); + ComparableVersion lowerCaseC = new ComparableVersion("c"); + // Digits are greater than letters + assertTrue(seven.compareTo(capitalJ) > 0, "expected 7 > J"); + assertTrue(capitalJ.compareTo(seven) < 0, "expected J < 1"); + assertTrue(seven.compareTo(lowerCaseC) > 0, "expected 7 > c"); + assertTrue(lowerCaseC.compareTo(seven) < 0, "expected c < 7"); + } + + @Test + void testNonAsciiDigits() { // These should not be treated as digits. + ComparableVersion asciiOne = new ComparableVersion("1"); + ComparableVersion arabicEight = new ComparableVersion("\u0668"); + ComparableVersion asciiNine = new ComparableVersion("9"); + assertTrue(asciiOne.compareTo(arabicEight) > 0, "expected " + "1" + " > " + "\u0668"); + assertTrue(arabicEight.compareTo(asciiOne) < 0, "expected " + "\u0668" + " < " + "1"); + assertTrue(asciiNine.compareTo(arabicEight) > 0, "expected " + "9" + " > " + "\u0668"); + assertTrue(arabicEight.compareTo(asciiNine) < 0, "expected " + "\u0668" + " < " + "9"); + } + + @Test + void testLexicographicOrder() { + ComparableVersion aardvark = new ComparableVersion("aardvark"); + ComparableVersion zebra = new ComparableVersion("zebra"); + assertTrue(zebra.compareTo(aardvark) > 0); + assertTrue(aardvark.compareTo(zebra) < 0); + + // Greek zebra + ComparableVersion greek = new ComparableVersion("ζέβρα"); + assertTrue(greek.compareTo(zebra) > 0); + assertTrue(zebra.compareTo(greek) < 0); + } + + /** + * Test MNG-5568 edge case + * which was showing transitive inconsistency: since A > B and B > C then we should have A > C + * otherwise sorting a list of ComparableVersions() will in some cases throw runtime exception; + * see Netbeans issues 240845 and + * 226100 + */ + @Test + void testMng5568() { + String a = "6.1.0"; + String b = "6.1.0rc3"; + String c = "6.1H.5-beta"; // this is the unusual version string, with 'H' in the middle + + checkVersionsOrder(b, a); // classical + checkVersionsOrder(b, c); // now b < c, but before MNG-5568, we had b > c + checkVersionsOrder(a, c); + } + + /** + * Test MNG-6572 optimization. + */ + @Test + void testMng6572() { + String a = "20190126.230843"; // resembles a SNAPSHOT + String b = "1234567890.12345"; // 10 digit number + String c = "123456789012345.1H.5-beta"; // 15 digit number + String d = "12345678901234567890.1H.5-beta"; // 20 digit number + + checkVersionsOrder(a, b); + checkVersionsOrder(b, c); + checkVersionsOrder(a, c); + checkVersionsOrder(c, d); + checkVersionsOrder(b, d); + checkVersionsOrder(a, d); + } + + /** + * Test all versions are equal when starting with many leading zeroes regardless of string length + * (related to MNG-6572 optimization) + */ + @Test + void testVersionEqualWithLeadingZeroes() { + // versions with string lengths from 1 to 19 + String[] arr = new String[] { + "0000000000000000001", + "000000000000000001", + "00000000000000001", + "0000000000000001", + "000000000000001", + "00000000000001", + "0000000000001", + "000000000001", + "00000000001", + "0000000001", + "000000001", + "00000001", + "0000001", + "000001", + "00001", + "0001", + "001", + "01", + "1" + }; + + checkVersionsArrayEqual(arr); + } + + /** + * Test all "0" versions are equal when starting with many leading zeroes regardless of string length + * (related to MNG-6572 optimization) + */ + @Test + void testVersionZeroEqualWithLeadingZeroes() { + // versions with string lengths from 1 to 19 + String[] arr = new String[] { + "0000000000000000000", + "000000000000000000", + "00000000000000000", + "0000000000000000", + "000000000000000", + "00000000000000", + "0000000000000", + "000000000000", + "00000000000", + "0000000000", + "000000000", + "00000000", + "0000000", + "000000", + "00000", + "0000", + "000", + "00", + "0" + }; + + checkVersionsArrayEqual(arr); + } + + /** + * Test MNG-6964 edge cases + * for qualifiers that start with "-0.", which was showing A == C and B == C but A < B. + */ + @Test + void testMng6964() { + String a = "1-0.alpha"; + String b = "1-0.beta"; + String c = "1"; + + checkVersionsOrder(a, c); // Now a < c, but before MNG-6964 they were equal + checkVersionsOrder(b, c); // Now b < c, but before MNG-6964 they were equal + checkVersionsOrder(a, b); // Should still be true + } + + @Test + void testLocaleIndependent() { + Locale orig = Locale.getDefault(); + Locale[] locales = {Locale.ENGLISH, new Locale("tr"), Locale.getDefault()}; + try { + for (Locale locale : locales) { + Locale.setDefault(locale); + checkVersionsEqual("1-abcdefghijklmnopqrstuvwxyz", "1-ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + } + } finally { + Locale.setDefault(orig); + } + } + + @Test + void testReuse() { + ComparableVersion c1 = new ComparableVersion("1"); + c1.parseVersion("2"); + + Comparable c2 = newComparable("2"); + + assertEquals(c1, c2, "reused instance should be equivalent to new instance"); + } + + /** + * Test MNG-7644 edge cases + * 1.0.0.RC1 < 1.0.0-RC2 and more generally: + * 1.0.0.X1 < 1.0.0-X2 for any string X + */ + @Test + void testMng7644() { + for (String x : new String[] {"abc", "alpha", "a", "beta", "b", "def", "milestone", "m", "RC"}) { + // 1.0.0.X1 < 1.0.0-X2 for any string x + checkVersionsOrder("1.0.0." + x + "1", "1.0.0-" + x + "2"); + // 2.0.X == 2-X == 2.0.0.X for any string x + checkVersionsEqual("2-" + x, "2.0." + x); // previously ordered, now equals + checkVersionsEqual("2-" + x, "2.0.0." + x); // previously ordered, now equals + checkVersionsEqual("2.0." + x, "2.0.0." + x); // previously ordered, now equals + } + } + + @Test + public void testMng7714() { + ComparableVersion f = new ComparableVersion("1.0.final-redhat"); + ComparableVersion sp1 = new ComparableVersion("1.0-sp1-redhat"); + ComparableVersion sp2 = new ComparableVersion("1.0-sp-1-redhat"); + ComparableVersion sp3 = new ComparableVersion("1.0-sp.1-redhat"); + assertTrue(f.compareTo(sp1) < 0, "expected " + f + " < " + sp1); + assertTrue(f.compareTo(sp2) < 0, "expected " + f + " < " + sp2); + assertTrue(f.compareTo(sp3) < 0, "expected " + f + " < " + sp3); + } +} diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java new file mode 100644 index 000000000000..370b0f0c5cc3 --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test DefaultArtifactVersion. + * + */ +class DefaultArtifactVersionTest { + private ArtifactVersion newArtifactVersion(String version) { + return new DefaultArtifactVersion(version); + } + + private void checkVersionParsing( + String version, int major, int minor, int incremental, int buildnumber, String qualifier) { + ArtifactVersion artifactVersion = newArtifactVersion(version); + String parsed = "'" + version + "' parsed as ('" + artifactVersion.getMajorVersion() + "', '" + + artifactVersion.getMinorVersion() + "', '" + artifactVersion.getIncrementalVersion() + "', '" + + artifactVersion.getBuildNumber() + "', '" + artifactVersion.getQualifier() + "'), "; + assertEquals(major, artifactVersion.getMajorVersion(), parsed + "check major version"); + assertEquals(minor, artifactVersion.getMinorVersion(), parsed + "check minor version"); + assertEquals(incremental, artifactVersion.getIncrementalVersion(), parsed + "check incremental version"); + assertEquals(buildnumber, artifactVersion.getBuildNumber(), parsed + "check build number"); + assertEquals(qualifier, artifactVersion.getQualifier(), parsed + "check qualifier"); + assertEquals(version, artifactVersion.toString(), "check " + version + " string value"); + } + + @Test + void testVersionParsing() { + checkVersionParsing("1", 1, 0, 0, 0, null); + checkVersionParsing("1.2", 1, 2, 0, 0, null); + checkVersionParsing("1.2.3", 1, 2, 3, 0, null); + checkVersionParsing("1.2.3-1", 1, 2, 3, 1, null); + checkVersionParsing("1.2.3-alpha-1", 1, 2, 3, 0, "alpha-1"); + checkVersionParsing("1.2-alpha-1", 1, 2, 0, 0, "alpha-1"); + checkVersionParsing("1.2-alpha-1-20050205.060708-1", 1, 2, 0, 0, "alpha-1-20050205.060708-1"); + checkVersionParsing("RELEASE", 0, 0, 0, 0, "RELEASE"); + checkVersionParsing("2.0-1", 2, 0, 0, 1, null); + + // 0 at the beginning of a number has a special handling + checkVersionParsing("02", 0, 0, 0, 0, "02"); + checkVersionParsing("0.09", 0, 0, 0, 0, "0.09"); + checkVersionParsing("0.2.09", 0, 0, 0, 0, "0.2.09"); + checkVersionParsing("2.0-01", 2, 0, 0, 0, "01"); + + // version schemes not really supported: fully transformed as qualifier + checkVersionParsing("1.0.1b", 0, 0, 0, 0, "1.0.1b"); + checkVersionParsing("1.0M2", 0, 0, 0, 0, "1.0M2"); + checkVersionParsing("1.0RC2", 0, 0, 0, 0, "1.0RC2"); + checkVersionParsing("1.1.2.beta1", 1, 1, 2, 0, "beta1"); + checkVersionParsing("1.7.3.beta1", 1, 7, 3, 0, "beta1"); + checkVersionParsing("1.7.3.0", 0, 0, 0, 0, "1.7.3.0"); + checkVersionParsing("1.7.3.0-1", 0, 0, 0, 0, "1.7.3.0-1"); + checkVersionParsing("PATCH-1193602", 0, 0, 0, 0, "PATCH-1193602"); + checkVersionParsing("5.0.0alpha-2006020117", 0, 0, 0, 0, "5.0.0alpha-2006020117"); + checkVersionParsing("1.0.0.-SNAPSHOT", 0, 0, 0, 0, "1.0.0.-SNAPSHOT"); + checkVersionParsing("1..0-SNAPSHOT", 0, 0, 0, 0, "1..0-SNAPSHOT"); + checkVersionParsing("1.0.-SNAPSHOT", 0, 0, 0, 0, "1.0.-SNAPSHOT"); + checkVersionParsing(".1.0-SNAPSHOT", 0, 0, 0, 0, ".1.0-SNAPSHOT"); + + checkVersionParsing("1.2.3.200705301630", 0, 0, 0, 0, "1.2.3.200705301630"); + checkVersionParsing("1.2.3-200705301630", 1, 2, 3, 0, "200705301630"); + } + + @Test + void testVersionComparing() { + assertVersionEqual("1", "1"); + assertVersionOlder("1", "2"); + assertVersionOlder("1.5", "2"); + assertVersionOlder("1", "2.5"); + assertVersionEqual("1", "1.0"); + assertVersionEqual("1", "1.0.0"); + assertVersionOlder("1.0", "1.1"); + assertVersionOlder("1.1", "1.2"); + assertVersionOlder("1.0.0", "1.1"); + assertVersionOlder("1.1", "1.2.0"); + + assertVersionOlder("1.1.2.alpha1", "1.1.2"); + assertVersionOlder("1.1.2.alpha1", "1.1.2.beta1"); + assertVersionOlder("1.1.2.beta1", "1.2"); + + assertVersionOlder("1.0-alpha-1", "1.0"); + assertVersionOlder("1.0-alpha-1", "1.0-alpha-2"); + assertVersionOlder("1.0-alpha-2", "1.0-alpha-15"); + assertVersionOlder("1.0-alpha-1", "1.0-beta-1"); + + assertVersionOlder("1.0-beta-1", "1.0-SNAPSHOT"); + assertVersionOlder("1.0-SNAPSHOT", "1.0"); + assertVersionOlder("1.0-alpha-1-SNAPSHOT", "1.0-alpha-1"); + + assertVersionOlder("1.0", "1.0-1"); + assertVersionOlder("1.0-1", "1.0-2"); + assertVersionEqual("2.0-0", "2.0"); + assertVersionOlder("2.0", "2.0-1"); + assertVersionOlder("2.0.0", "2.0-1"); + assertVersionOlder("2.0-1", "2.0.1"); + + assertVersionOlder("2.0.1-klm", "2.0.1-lmn"); + assertVersionOlder("2.0.1", "2.0.1-xyz"); + assertVersionOlder("2.0.1-xyz-1", "2.0.1-1-xyz"); + + assertVersionOlder("2.0.1", "2.0.1-123"); + assertVersionOlder("2.0.1-xyz", "2.0.1-123"); + + assertVersionOlder("1.2.3-10000000000", "1.2.3-10000000001"); + assertVersionOlder("1.2.3-1", "1.2.3-10000000001"); + assertVersionOlder("2.3.0-v200706262000", "2.3.0-v200706262130"); // org.eclipse:emf:2.3.0-v200706262000 + // org.eclipse.wst.common_core.feature_2.0.0.v200706041905-7C78EK9E_EkMNfNOd2d8qq + assertVersionOlder("2.0.0.v200706041905-7C78EK9E_EkMNfNOd2d8qq", "2.0.0.v200706041906-7C78EK9E_EkMNfNOd2d8qq"); + } + + @Test + void testVersionSnapshotComparing() { + assertVersionEqual("1-SNAPSHOT", "1-SNAPSHOT"); + assertVersionOlder("1-SNAPSHOT", "2-SNAPSHOT"); + assertVersionOlder("1.5-SNAPSHOT", "2-SNAPSHOT"); + assertVersionOlder("1-SNAPSHOT", "2.5-SNAPSHOT"); + assertVersionEqual("1-SNAPSHOT", "1.0-SNAPSHOT"); + assertVersionEqual("1-SNAPSHOT", "1.0.0-SNAPSHOT"); + assertVersionOlder("1.0-SNAPSHOT", "1.1-SNAPSHOT"); + assertVersionOlder("1.1-SNAPSHOT", "1.2-SNAPSHOT"); + assertVersionOlder("1.0.0-SNAPSHOT", "1.1-SNAPSHOT"); + assertVersionOlder("1.1-SNAPSHOT", "1.2.0-SNAPSHOT"); + + // assertVersionOlder( "1.0-alpha-1-SNAPSHOT", "1.0-SNAPSHOT" ); + assertVersionOlder("1.0-alpha-1-SNAPSHOT", "1.0-alpha-2-SNAPSHOT"); + assertVersionOlder("1.0-alpha-1-SNAPSHOT", "1.0-beta-1-SNAPSHOT"); + + assertVersionOlder("1.0-beta-1-SNAPSHOT", "1.0-SNAPSHOT-SNAPSHOT"); + assertVersionOlder("1.0-SNAPSHOT-SNAPSHOT", "1.0-SNAPSHOT"); + assertVersionOlder("1.0-alpha-1-SNAPSHOT-SNAPSHOT", "1.0-alpha-1-SNAPSHOT"); + + assertVersionOlder("1.0-SNAPSHOT", "1.0-1-SNAPSHOT"); + assertVersionOlder("1.0-1-SNAPSHOT", "1.0-2-SNAPSHOT"); + // assertVersionEqual( "2.0-0-SNAPSHOT", "2.0-SNAPSHOT" ); + assertVersionOlder("2.0-SNAPSHOT", "2.0-1-SNAPSHOT"); + assertVersionOlder("2.0.0-SNAPSHOT", "2.0-1-SNAPSHOT"); + assertVersionOlder("2.0-1-SNAPSHOT", "2.0.1-SNAPSHOT"); + + assertVersionOlder("2.0.1-klm-SNAPSHOT", "2.0.1-lmn-SNAPSHOT"); + // assertVersionOlder( "2.0.1-xyz-SNAPSHOT", "2.0.1-SNAPSHOT" ); + assertVersionOlder("2.0.1-SNAPSHOT", "2.0.1-123-SNAPSHOT"); + assertVersionOlder("2.0.1-xyz-SNAPSHOT", "2.0.1-123-SNAPSHOT"); + } + + @Test + void testSnapshotVsReleases() { + assertVersionOlder("1.0-RC1", "1.0-SNAPSHOT"); + assertVersionOlder("1.0-rc1", "1.0-SNAPSHOT"); + assertVersionOlder("1.0-rc-1", "1.0-SNAPSHOT"); + } + + @Test + void testHashCode() { + ArtifactVersion v1 = newArtifactVersion("1"); + ArtifactVersion v2 = newArtifactVersion("1.0"); + assertTrue(v1.equals(v2)); + assertEquals(v1.hashCode(), v2.hashCode()); + } + + @Test + void testEqualsNullSafe() { + assertFalse(newArtifactVersion("1").equals(null)); + } + + @Test + void testEqualsTypeSafe() { + assertFalse(newArtifactVersion("1").equals("non-an-artifact-version-instance")); + } + + private void assertVersionOlder(String left, String right) { + assertTrue( + newArtifactVersion(left).compareTo(newArtifactVersion(right)) < 0, + left + " should be older than " + right); + assertTrue( + newArtifactVersion(right).compareTo(newArtifactVersion(left)) > 0, + right + " should be newer than " + left); + } + + private void assertVersionEqual(String left, String right) { + assertTrue( + newArtifactVersion(left).compareTo(newArtifactVersion(right)) == 0, + left + " should be equal to " + right); + assertTrue( + newArtifactVersion(right).compareTo(newArtifactVersion(left)) == 0, + right + " should be equal to " + left); + } +} diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/VersionRangeTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/VersionRangeTest.java new file mode 100644 index 000000000000..8ccf5bcca4f2 --- /dev/null +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/VersionRangeTest.java @@ -0,0 +1,742 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests version range construction. + * + */ +class VersionRangeTest { + private static final String CHECK_NUM_RESTRICTIONS = "check number of restrictions"; + + private static final String CHECK_UPPER_BOUND = "check upper bound"; + + private static final String CHECK_UPPER_BOUND_INCLUSIVE = "check upper bound is inclusive"; + + private static final String CHECK_LOWER_BOUND = "check lower bound"; + + private static final String CHECK_LOWER_BOUND_INCLUSIVE = "check lower bound is inclusive"; + + private static final String CHECK_VERSION_RECOMMENDATION = "check version recommended"; + + private static final String CHECK_SELECTED_VERSION_KNOWN = "check selected version known"; + + private static final String CHECK_SELECTED_VERSION = "check selected version"; + + @Test + void testRange() throws InvalidVersionSpecificationException, OverConstrainedVersionException { + Artifact artifact = null; + + VersionRange range = VersionRange.createFromVersionSpec("(,1.0]"); + List restrictions = range.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + Restriction restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + assertFalse(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertNull(range.getSelectedVersion(artifact), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("1.0"); + assertEquals("1.0", range.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = range.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertTrue(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertEquals("1.0", range.getSelectedVersion(artifact).toString(), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("[1.0]"); + restrictions = range.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.0", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + assertFalse(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertNull(range.getSelectedVersion(artifact), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("[1.2,1.3]"); + restrictions = range.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + assertFalse(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertNull(range.getSelectedVersion(artifact), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("[1.0,2.0)"); + restrictions = range.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.0", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("2.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + assertFalse(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertNull(range.getSelectedVersion(artifact), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("[1.5,)"); + restrictions = range.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.5", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + assertFalse(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertNull(range.getSelectedVersion(artifact), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("(,1.0],[1.2,)"); + restrictions = range.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restriction = restrictions.get(1); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + assertNull(range.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + assertFalse(range.isSelectedVersionKnown(artifact), CHECK_SELECTED_VERSION_KNOWN); + assertNull(range.getSelectedVersion(artifact), CHECK_SELECTED_VERSION); + + range = VersionRange.createFromVersionSpec("[1.0,)"); + assertFalse(range.containsVersion(new DefaultArtifactVersion("1.0-SNAPSHOT"))); + + range = VersionRange.createFromVersionSpec("[1.0,1.1-SNAPSHOT]"); + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.1-SNAPSHOT"))); + + range = VersionRange.createFromVersionSpec("[5.0.9.0,5.0.10.0)"); + assertTrue(range.containsVersion(new DefaultArtifactVersion("5.0.9.0"))); + } + + @Test + void testSameUpperAndLowerBoundRoundtrip() throws InvalidVersionSpecificationException { + VersionRange range = VersionRange.createFromVersionSpec("[1.0]"); + VersionRange range2 = VersionRange.createFromVersionSpec(range.toString()); + assertEquals(range, range2); + } + + @Test + void testInvalidRanges() { + checkInvalidRange("(1.0)"); + checkInvalidRange("[1.0)"); + checkInvalidRange("(1.0]"); + checkInvalidRange("(1.0,1.0]"); + checkInvalidRange("[1.0,1.0)"); + checkInvalidRange("(1.0,1.0)"); + checkInvalidRange("[1.1,1.0]"); + checkInvalidRange("[1.0,1.2),1.3"); + // overlap + checkInvalidRange("[1.0,1.2),(1.1,1.3]"); + // overlap + checkInvalidRange("[1.1,1.3),(1.0,1.2]"); + // ordering + checkInvalidRange("(1.1,1.2],[1.0,1.1)"); + } + + @Test + @SuppressWarnings("checkstyle:MethodLength") + void testIntersections() throws InvalidVersionSpecificationException { + VersionRange range1 = VersionRange.createFromVersionSpec("1.0"); + VersionRange range2 = VersionRange.createFromVersionSpec("1.1"); + VersionRange mergedRange = range1.restrict(range2); + // TODO current policy is to retain the original version - is this correct, do we need strategies or is that + // handled elsewhere? + // assertEquals( "1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION ); + assertEquals("1.0", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + List restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + Restriction restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + mergedRange = range2.restrict(range1); + assertEquals("1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + // TODO test reversed restrictions on all below + range1 = VersionRange.createFromVersionSpec("[1.0,)"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertEquals("1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.0", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.1,)"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertEquals("1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.1]"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertEquals("1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(1.1,)"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.2,)"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.2]"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertEquals("1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.1]"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertEquals("1.1", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.1", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.1)"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.1", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.0]"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.0], [1.1,)"); + range2 = VersionRange.createFromVersionSpec("1.2"); + mergedRange = range1.restrict(range2); + assertEquals("1.2", mergedRange.getRecommendedVersion().toString(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.0], [1.1,)"); + range2 = VersionRange.createFromVersionSpec("1.0.5"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.0", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.1), (1.1,)"); + range2 = VersionRange.createFromVersionSpec("1.1"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertNull(restriction.getLowerBound(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.1", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertNull(restriction.getUpperBound(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.1,1.3]"); + range2 = VersionRange.createFromVersionSpec("(1.1,)"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.3)"); + range2 = VersionRange.createFromVersionSpec("[1.2,1.3]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.1,1.3]"); + range2 = VersionRange.createFromVersionSpec("[1.2,)"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.3]"); + range2 = VersionRange.createFromVersionSpec("[1.2,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(1.2,1.3]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(1.2,1.3)"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.2,1.3)"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.2", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.3", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.1]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.1", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.1)"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("[1.1]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.1", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("[1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.4", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2),(1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("(1.1,1.4)"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2),(1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("(1.1,1.4)"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertFalse(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertFalse(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("(,1.1),(1.4,)"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + range1 = VersionRange.createFromVersionSpec("(,1.1],[1.4,)"); + range2 = VersionRange.createFromVersionSpec("(1.1,1.4)"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + range1 = VersionRange.createFromVersionSpec("[,1.1],[1.4,]"); + range2 = VersionRange.createFromVersionSpec("[1.2,1.3]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4],[1.6,]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(2, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.5]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4],[1.5,]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(3, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(2); + assertEquals("1.5", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.5", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + range1 = VersionRange.createFromVersionSpec("[1.0,1.2],[1.3,1.7]"); + range2 = VersionRange.createFromVersionSpec("[1.1,1.4],[1.5,1.6]"); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(3, restrictions.size(), CHECK_NUM_RESTRICTIONS); + restriction = restrictions.get(0); + assertEquals("1.1", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.2", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(1); + assertEquals("1.3", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.4", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + restriction = restrictions.get(2); + assertEquals("1.5", restriction.getLowerBound().toString(), CHECK_LOWER_BOUND); + assertTrue(restriction.isLowerBoundInclusive(), CHECK_LOWER_BOUND_INCLUSIVE); + assertEquals("1.6", restriction.getUpperBound().toString(), CHECK_UPPER_BOUND); + assertTrue(restriction.isUpperBoundInclusive(), CHECK_UPPER_BOUND_INCLUSIVE); + + // test restricting empty sets + range1 = VersionRange.createFromVersionSpec("[,1.1],[1.4,]"); + range2 = VersionRange.createFromVersionSpec("[1.2,1.3]"); + range1 = range1.restrict(range2); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + range1 = VersionRange.createFromVersionSpec("[,1.1],[1.4,]"); + range2 = VersionRange.createFromVersionSpec("[1.2,1.3]"); + range2 = range1.restrict(range2); + mergedRange = range1.restrict(range2); + assertNull(mergedRange.getRecommendedVersion(), CHECK_VERSION_RECOMMENDATION); + restrictions = mergedRange.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + } + + @Test + void testReleaseRangeBoundsContainsSnapshots() throws InvalidVersionSpecificationException { + VersionRange range = VersionRange.createFromVersionSpec("[1.0,1.2]"); + + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.1-SNAPSHOT"))); + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.2-SNAPSHOT"))); + assertFalse(range.containsVersion(new DefaultArtifactVersion("1.0-SNAPSHOT"))); + } + + @Test + void testSnapshotRangeBoundsCanContainSnapshots() throws InvalidVersionSpecificationException { + VersionRange range = VersionRange.createFromVersionSpec("[1.0,1.2-SNAPSHOT]"); + + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.1-SNAPSHOT"))); + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.2-SNAPSHOT"))); + + range = VersionRange.createFromVersionSpec("[1.0-SNAPSHOT,1.2]"); + + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.0-SNAPSHOT"))); + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.1-SNAPSHOT"))); + } + + @Test + void testSnapshotSoftVersionCanContainSnapshot() throws InvalidVersionSpecificationException { + VersionRange range = VersionRange.createFromVersionSpec("1.0-SNAPSHOT"); + + assertTrue(range.containsVersion(new DefaultArtifactVersion("1.0-SNAPSHOT"))); + } + + private void checkInvalidRange(String version) { + assertThrows( + InvalidVersionSpecificationException.class, + () -> VersionRange.createFromVersionSpec(version), + "Version " + version + " should have failed to construct"); + } + + @Test + void testContains() throws InvalidVersionSpecificationException { + ArtifactVersion actualVersion = new DefaultArtifactVersion("2.0.5"); + assertTrue(enforceVersion("2.0.5", actualVersion)); + assertTrue(enforceVersion("2.0.4", actualVersion)); + assertTrue(enforceVersion("[2.0.5]", actualVersion)); + assertFalse(enforceVersion("[2.0.6,)", actualVersion)); + assertFalse(enforceVersion("[2.0.6]", actualVersion)); + assertTrue(enforceVersion("[2.0,2.1]", actualVersion)); + assertFalse(enforceVersion("[2.0,2.0.3]", actualVersion)); + assertTrue(enforceVersion("[2.0,2.0.5]", actualVersion)); + assertFalse(enforceVersion("[2.0,2.0.5)", actualVersion)); + } + + public boolean enforceVersion(String requiredVersionRange, ArtifactVersion actualVersion) + throws InvalidVersionSpecificationException { + VersionRange vr = VersionRange.createFromVersionSpec(requiredVersionRange); + + return vr.containsVersion(actualVersion); + } + + @Test + void testCache() throws InvalidVersionSpecificationException { + VersionRange range = VersionRange.createFromVersionSpec("[1.0,1.2]"); + assertSame(range, VersionRange.createFromVersionSpec("[1.0,1.2]")); // same instance from spec cache + + VersionRange spec = VersionRange.createFromVersionSpec("1.0"); + assertSame(spec, VersionRange.createFromVersionSpec("1.0")); // same instance from spec cache + List restrictions = spec.getRestrictions(); + assertEquals(1, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + VersionRange version = VersionRange.createFromVersion("1.0"); + assertSame(version, VersionRange.createFromVersion("1.0")); // same instance from version cache + restrictions = version.getRestrictions(); + assertEquals(0, restrictions.size(), CHECK_NUM_RESTRICTIONS); + + assertFalse( + spec.equals(version), + "check !VersionRange.createFromVersionSpec(x).equals(VersionRange.createFromVersion(x))"); + } +} diff --git a/compat/maven-builder-support/pom.xml b/compat/maven-builder-support/pom.xml new file mode 100644 index 000000000000..2f4b6bd61a23 --- /dev/null +++ b/compat/maven-builder-support/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + + org.apache.maven + maven-compat-modules + 4.1.0-SNAPSHOT + + + maven-builder-support + + Maven Builder Support (deprecated) + Support for descriptor builders (model, setting, toolchains) + + + + org.apache.maven + maven-api-core + provided + + + org.junit.jupiter + junit-jupiter-api + test + + + diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java new file mode 100644 index 000000000000..8cfa77d3c767 --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +/** + * Describes a problem that was encountered during settings building. A problem can either be an exception that was + * thrown or a simple string message. In addition, a problem carries a hint about its source, e.g. the settings file + * that exhibits the problem. + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +class DefaultProblem implements Problem { + + private final String source; + + private final int lineNumber; + + private final int columnNumber; + + private final String message; + + private final Exception exception; + + private final Severity severity; + + /** + * Creates a new problem with the specified message and exception. + * Either {@code message} or {@code exception} is required + * + * @param message The message describing the problem, may be {@code null}. + * @param severity The severity level of the problem, may be {@code null} to default to + * {@link org.apache.maven.building.Problem.Severity#ERROR}. + * @param source A hint about the source of the problem like a file path, may be {@code null}. + * @param lineNumber The one-based index of the line containing the problem or {@code -1} if unknown. + * @param columnNumber The one-based index of the column containing the problem or {@code -1} if unknown. + * @param exception The exception that caused this problem, may be {@code null}. + */ + DefaultProblem( + String message, Severity severity, String source, int lineNumber, int columnNumber, Exception exception) { + this.message = message; + this.severity = (severity != null) ? severity : Severity.ERROR; + this.source = (source != null) ? source : ""; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.exception = exception; + } + + @Override + public String getSource() { + return source; + } + + @Override + public int getLineNumber() { + return lineNumber; + } + + @Override + public int getColumnNumber() { + return columnNumber; + } + + @Override + public String getLocation() { + StringBuilder buffer = new StringBuilder(256); + + if (!getSource().isEmpty()) { + if (!buffer.isEmpty()) { + buffer.append(", "); + } + buffer.append(getSource()); + } + + if (getLineNumber() > 0) { + if (!buffer.isEmpty()) { + buffer.append(", "); + } + buffer.append("line ").append(getLineNumber()); + } + + if (getColumnNumber() > 0) { + if (!buffer.isEmpty()) { + buffer.append(", "); + } + buffer.append("column ").append(getColumnNumber()); + } + + return buffer.toString(); + } + + @Override + public Exception getException() { + return exception; + } + + @Override + public String getMessage() { + String msg; + + if (message != null && !message.isEmpty()) { + msg = message; + } else { + msg = exception.getMessage(); + + if (msg == null) { + msg = ""; + } + } + + return msg; + } + + @Override + public Severity getSeverity() { + return severity; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(128); + + buffer.append('[').append(getSeverity()).append("] "); + buffer.append(getMessage()); + String location = getLocation(); + if (!location.isEmpty()) { + buffer.append(" @ "); + buffer.append(location); + } + + return buffer.toString(); + } +} diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblemCollector.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblemCollector.java new file mode 100644 index 000000000000..b545bc1c4df8 --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblemCollector.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.util.ArrayList; +import java.util.List; + +/** + * Collects problems that are encountered during settings building. + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +class DefaultProblemCollector implements ProblemCollector { + + private final List problems; + + private String source; + + DefaultProblemCollector(List problems) { + this.problems = (problems != null) ? problems : new ArrayList<>(); + } + + @Override + public List getProblems() { + return problems; + } + + @Override + public void setSource(String source) { + this.source = source; + } + + @Override + public void add(Problem.Severity severity, String message, int line, int column, Exception cause) { + Problem problem = new DefaultProblem(message, severity, source, line, column, cause); + + problems.add(problem); + } +} diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/FileSource.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/FileSource.java new file mode 100644 index 000000000000..6074ba312d5b --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/FileSource.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/** + * Wraps an ordinary {@link File} as a source. + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +public class FileSource implements Source { + private final Path path; + + private final int hashCode; + + /** + * Creates a new source backed by the specified file. + * + * @param file The file, must not be {@code null}. + * @deprecated Use {@link #FileSource(Path)} instead. + */ + @Deprecated + public FileSource(File file) { + this(Objects.requireNonNull(file, "file cannot be null").toPath()); + } + + /** + * Creates a new source backed by the specified file. + * + * @param path The file, must not be {@code null}. + * @since 4.0.0 + */ + public FileSource(Path path) { + this.path = Objects.requireNonNull(path, "path cannot be null").toAbsolutePath(); + this.hashCode = Objects.hash(path); + } + + @Override + public InputStream getInputStream() throws IOException { + return Files.newInputStream(path); + } + + @Override + public String getLocation() { + return path.toString(); + } + + /** + * Gets the file of this source. + * + * @return The underlying file, never {@code null}. + * @deprecated Use {@link #getPath()} instead. + */ + @Deprecated + public File getFile() { + return path.toFile(); + } + + /** + * Gets the file of this source. + * + * @return The underlying file, never {@code null}. + * @since 4.0.0 + */ + public Path getPath() { + return path; + } + + @Override + public String toString() { + return getLocation(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (!FileSource.class.equals(obj.getClass())) { + return false; + } + + FileSource other = (FileSource) obj; + return this.path.equals(other.path); + } +} diff --git a/maven-builder-support/src/main/java/org/apache/maven/building/Problem.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/Problem.java similarity index 95% rename from maven-builder-support/src/main/java/org/apache/maven/building/Problem.java rename to compat/maven-builder-support/src/main/java/org/apache/maven/building/Problem.java index 9ab9b3a85c78..40c87f14fd0e 100644 --- a/maven-builder-support/src/main/java/org/apache/maven/building/Problem.java +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/Problem.java @@ -1,5 +1,3 @@ -package org.apache.maven.building; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,28 +16,25 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.building; /** * Describes a problem that was encountered during settings building. A problem can either be an exception that was * thrown or a simple string message. In addition, a problem carries a hint about its source, e.g. the settings file * that exhibits the problem. * - * @author Benjamin Bentmann - * @author Robert Scholte + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead */ -public interface Problem -{ +@Deprecated(since = "4.0.0") +public interface Problem { /** * The different severity levels for a problem, in decreasing order. */ - enum Severity - { - + enum Severity { FATAL, // ERROR, // WARNING // - } /** @@ -97,5 +92,4 @@ enum Severity * @return The severity level of this problem, never {@code null}. */ Severity getSeverity(); - } diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollector.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollector.java new file mode 100644 index 000000000000..1e0b5473a5ac --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollector.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.util.List; + +/** + * Collects problems that are encountered during settings building. + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +public interface ProblemCollector { + + /** + * Adds the specified problem. + * Either message or exception is required + * + * @param severity The severity of the problem, must not be {@code null}. + * @param message The detail message of the problem, may be {@code null}. + * @param line The one-based index of the line containing the problem or {@code -1} if unknown. + * @param column The one-based index of the column containing the problem or {@code -1} if unknown. + * @param cause The cause of the problem, may be {@code null}. + */ + void add(Problem.Severity severity, String message, int line, int column, Exception cause); + + /** + * The next messages will be bound to this source. When calling this method again, previous messages keep + * their source, but the next messages will use the new source. + * + * @param source a source + */ + void setSource(String source); + + /** + * + * @return the collected Problems, never {@code null} + */ + List getProblems(); +} diff --git a/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollectorFactory.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollectorFactory.java similarity index 78% rename from maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollectorFactory.java rename to compat/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollectorFactory.java index c1c23737ecb1..bbae68915c67 100644 --- a/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollectorFactory.java +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/ProblemCollectorFactory.java @@ -1,5 +1,3 @@ -package org.apache.maven.building; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,26 +16,25 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.building; import java.util.List; /** - * - * @author Robert Scholte + * * @since 3.3.0 + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead */ -public class ProblemCollectorFactory -{ +@Deprecated(since = "4.0.0") +public class ProblemCollectorFactory { /** - * The default implementation is not visible, create it with this factory - * + * The default implementation is not visible, create it with this factory + * * @param problems starting set of problems, may be {@code null} * @return a new instance of a ProblemCollector */ - public static ProblemCollector newInstance( List problems ) - { - return new DefaultProblemCollector( problems ); + public static ProblemCollector newInstance(List problems) { + return new DefaultProblemCollector(problems); } - } diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/Source.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/Source.java new file mode 100644 index 000000000000..b46973e43427 --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/Source.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Provides access to the contents of a source independently of the backing store (e.g. file system, database, memory). + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +public interface Source { + + /** + * Gets a byte stream to the source contents. Closing the returned stream is the responsibility of the caller. + * + * @return A byte stream to the source contents, never {@code null}. + * @throws IOException in case of IO issue + */ + InputStream getInputStream() throws IOException; + + /** + * Provides a user-friendly hint about the location of the source. This could be a local file path, a URI or just an + * empty string. The intention is to assist users during error reporting. + * + * @return A user-friendly hint about the location of the source, never {@code null}. + */ + String getLocation(); +} diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/StringSource.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/StringSource.java new file mode 100644 index 000000000000..6574df593a67 --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/StringSource.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/** + * Wraps an ordinary {@link CharSequence} as a source. + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +public class StringSource implements Source { + private final String content; + + private final String location; + + private final int hashCode; + + /** + * Creates a new source backed by the specified string. + * + * @param content The String representation, may be empty or {@code null}. + */ + public StringSource(CharSequence content) { + this(content, null); + } + + /** + * Creates a new source backed by the specified string. + * + * @param content The String representation, may be empty or {@code null}. + * @param location The location to report for this use, may be {@code null}. + */ + public StringSource(CharSequence content, String location) { + this.content = (content != null) ? content.toString() : ""; + this.location = (location != null) ? location : "(memory)"; + this.hashCode = this.content.hashCode(); + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String getLocation() { + return location; + } + + /** + * Gets the content of this source. + * + * @return The underlying character stream, never {@code null}. + */ + public String getContent() { + return content; + } + + @Override + public String toString() { + return getLocation(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (!StringSource.class.equals(obj.getClass())) { + return false; + } + + StringSource other = (StringSource) obj; + return this.content.equals(other.content); + } +} diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/UrlSource.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/UrlSource.java new file mode 100644 index 000000000000..19f160485b08 --- /dev/null +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/UrlSource.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Objects; + +/** + * Wraps an ordinary {@link URL} as a source. + * + * @deprecated since 4.0.0, use {@link org.apache.maven.api.services} instead + */ +@Deprecated(since = "4.0.0") +public class UrlSource implements Source { + + private final URL url; + + private final int hashCode; + + /** + * Creates a new source backed by the specified URL. + * + * @param url The file, must not be {@code null}. + */ + public UrlSource(URL url) { + this.url = Objects.requireNonNull(url, "url cannot be null"); + this.hashCode = Objects.hashCode(url); + } + + @Override + public InputStream getInputStream() throws IOException { + return url.openStream(); + } + + @Override + public String getLocation() { + return url.toString(); + } + + /** + * Gets the URL of this source. + * + * @return The underlying URL, never {@code null}. + */ + public URL getUrl() { + return url; + } + + @Override + public String toString() { + return getLocation(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (!UrlSource.class.equals(obj.getClass())) { + return false; + } + + UrlSource other = (UrlSource) obj; + return Objects.equals(url.toExternalForm(), other.url.toExternalForm()); + } +} diff --git a/compat/maven-builder-support/src/site/site.xml b/compat/maven-builder-support/src/site/site.xml new file mode 100644 index 000000000000..4ee3b709cfc4 --- /dev/null +++ b/compat/maven-builder-support/src/site/site.xml @@ -0,0 +1,35 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/DefaultProblemCollectorTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/DefaultProblemCollectorTest.java new file mode 100644 index 000000000000..4ff6750e5ad0 --- /dev/null +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/DefaultProblemCollectorTest.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import org.apache.maven.building.Problem.Severity; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +class DefaultProblemCollectorTest { + + @Test + void testGetProblems() { + DefaultProblemCollector collector = new DefaultProblemCollector(null); + assertNotNull(collector.getProblems()); + assertEquals(0, collector.getProblems().size()); + + collector.add(null, "MESSAGE1", -1, -1, null); + + Exception e2 = new Exception(); + collector.add(Severity.WARNING, null, 42, 127, e2); + + assertEquals(2, collector.getProblems().size()); + + Problem p1 = collector.getProblems().get(0); + assertEquals(Severity.ERROR, p1.getSeverity()); + assertEquals("MESSAGE1", p1.getMessage()); + assertEquals(-1, p1.getLineNumber()); + assertEquals(-1, p1.getColumnNumber()); + assertNull(p1.getException()); + + Problem p2 = collector.getProblems().get(1); + assertEquals(Severity.WARNING, p2.getSeverity()); + assertEquals("", p2.getMessage()); + assertEquals(42, p2.getLineNumber()); + assertEquals(127, p2.getColumnNumber()); + assertEquals(e2, p2.getException()); + } + + @Test + void testSetSource() { + DefaultProblemCollector collector = new DefaultProblemCollector(null); + + collector.add(null, "PROBLEM1", -1, -1, null); + + collector.setSource("SOURCE_PROBLEM2"); + collector.add(null, "PROBLEM2", -1, -1, null); + + collector.setSource("SOURCE_PROBLEM3"); + collector.add(null, "PROBLEM3", -1, -1, null); + + assertEquals("", collector.getProblems().get(0).getSource()); + assertEquals("SOURCE_PROBLEM2", collector.getProblems().get(1).getSource()); + assertEquals("SOURCE_PROBLEM3", collector.getProblems().get(2).getSource()); + } +} diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/DefaultProblemTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/DefaultProblemTest.java new file mode 100644 index 000000000000..3bd8a9aa9ec0 --- /dev/null +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/DefaultProblemTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import org.apache.maven.building.Problem.Severity; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +class DefaultProblemTest { + + @Test + void testGetSeverity() { + DefaultProblem problem = new DefaultProblem(null, null, null, -1, -1, null); + assertEquals(Severity.ERROR, problem.getSeverity()); + + problem = new DefaultProblem(null, Severity.FATAL, null, -1, -1, null); + assertEquals(Severity.FATAL, problem.getSeverity()); + + problem = new DefaultProblem(null, Severity.ERROR, null, -1, -1, null); + assertEquals(Severity.ERROR, problem.getSeverity()); + + problem = new DefaultProblem(null, Severity.WARNING, null, -1, -1, null); + assertEquals(Severity.WARNING, problem.getSeverity()); + } + + @Test + void testGetLineNumber() { + DefaultProblem problem = new DefaultProblem(null, null, null, -1, -1, null); + assertEquals(-1, problem.getLineNumber()); + + problem = new DefaultProblem(null, null, null, 42, -1, null); + assertEquals(42, problem.getLineNumber()); + + problem = new DefaultProblem(null, null, null, Integer.MAX_VALUE, -1, null); + assertEquals(Integer.MAX_VALUE, problem.getLineNumber()); + + // this case is not specified, might also return -1 + problem = new DefaultProblem(null, null, null, Integer.MIN_VALUE, -1, null); + assertEquals(Integer.MIN_VALUE, problem.getLineNumber()); + } + + @Test + void testGetColumnNumber() { + DefaultProblem problem = new DefaultProblem(null, null, null, -1, -1, null); + assertEquals(-1, problem.getColumnNumber()); + + problem = new DefaultProblem(null, null, null, -1, 42, null); + assertEquals(42, problem.getColumnNumber()); + + problem = new DefaultProblem(null, null, null, -1, Integer.MAX_VALUE, null); + assertEquals(Integer.MAX_VALUE, problem.getColumnNumber()); + + // this case is not specified, might also return -1 + problem = new DefaultProblem(null, null, null, -1, Integer.MIN_VALUE, null); + assertEquals(Integer.MIN_VALUE, problem.getColumnNumber()); + } + + @Test + void testGetException() { + DefaultProblem problem = new DefaultProblem(null, null, null, -1, -1, null); + assertNull(problem.getException()); + + Exception e = new Exception(); + problem = new DefaultProblem(null, null, null, -1, -1, e); + assertSame(e, problem.getException()); + } + + @Test + void testGetSource() { + DefaultProblem problem = new DefaultProblem(null, null, null, -1, -1, null); + assertEquals("", problem.getSource()); + + problem = new DefaultProblem(null, null, "", -1, -1, null); + assertEquals("", problem.getSource()); + + problem = new DefaultProblem(null, null, "SOURCE", -1, -1, null); + assertEquals("SOURCE", problem.getSource()); + } + + @Test + void testGetLocation() { + DefaultProblem problem = new DefaultProblem(null, null, null, -1, -1, null); + assertEquals("", problem.getLocation()); + + problem = new DefaultProblem(null, null, "SOURCE", -1, -1, null); + assertEquals("SOURCE", problem.getLocation()); + + problem = new DefaultProblem(null, null, null, 42, -1, null); + assertEquals("line 42", problem.getLocation()); + + problem = new DefaultProblem(null, null, null, -1, 127, null); + assertEquals("column 127", problem.getLocation()); + + problem = new DefaultProblem(null, null, "SOURCE", 42, 127, null); + assertEquals("SOURCE, line 42, column 127", problem.getLocation()); + } + + @Test + void testGetMessage() { + DefaultProblem problem = new DefaultProblem("MESSAGE", null, null, -1, -1, null); + assertEquals("MESSAGE", problem.getMessage()); + + problem = new DefaultProblem(null, null, null, -1, -1, new Exception()); + assertEquals("", problem.getMessage()); + + problem = new DefaultProblem(null, null, null, -1, -1, new Exception("EXCEPTION MESSAGE")); + assertEquals("EXCEPTION MESSAGE", problem.getMessage()); + } +} diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/FileSourceTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/FileSourceTest.java new file mode 100644 index 000000000000..33dd09373548 --- /dev/null +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/FileSourceTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.File; +import java.io.InputStream; +import java.util.Scanner; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FileSourceTest { + + @Test + void testFileSource() { + NullPointerException e = assertThrows( + NullPointerException.class, + () -> new FileSource((File) null), + "Should fail, since you must specify a file"); + assertEquals("file cannot be null", e.getMessage()); + } + + @Test + void testGetInputStream() throws Exception { + File txtFile = new File("target/test-classes/source.txt"); + FileSource source = new FileSource(txtFile); + + try (InputStream is = source.getInputStream(); + Scanner scanner = new Scanner(is)) { + + assertEquals("Hello World!", scanner.nextLine()); + } + } + + @Test + void testGetLocation() { + File txtFile = new File("target/test-classes/source.txt"); + FileSource source = new FileSource(txtFile); + assertEquals(txtFile.getAbsolutePath(), source.getLocation()); + } + + @Test + void testGetFile() { + File txtFile = new File("target/test-classes/source.txt"); + FileSource source = new FileSource(txtFile); + assertEquals(txtFile.getAbsoluteFile(), source.getFile()); + } +} diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java new file mode 100644 index 000000000000..140035bb03b5 --- /dev/null +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +class ProblemCollectorFactoryTest { + + @Test + void testNewInstance() { + ProblemCollector collector1 = ProblemCollectorFactory.newInstance(null); + + Problem problem = new DefaultProblem("MESSAGE1", null, null, -1, -1, null); + ProblemCollector collector2 = ProblemCollectorFactory.newInstance(Collections.singletonList(problem)); + + assertNotSame(collector1, collector2); + assertEquals(0, collector1.getProblems().size()); + assertEquals(1, collector2.getProblems().size()); + } + + @Test + void testAddProblem() { + ProblemCollector collector = ProblemCollectorFactory.newInstance(null); + collector.setSource("pom.xml"); + collector.add(Problem.Severity.ERROR, "Error message", 10, 5, null); + collector.add(Problem.Severity.WARNING, "Warning message", 15, 3, null); + + List problems = collector.getProblems(); + assertEquals(2, problems.size(), "Should collect both problems"); + assertEquals(Problem.Severity.ERROR, problems.get(0).getSeverity(), "First problem should be ERROR"); + assertEquals("Error message", problems.get(0).getMessage(), "First problem should have correct message"); + assertEquals(Problem.Severity.WARNING, problems.get(1).getSeverity(), "Second problem should be WARNING"); + } +} diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/StringSourceTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/StringSourceTest.java new file mode 100644 index 000000000000..8bd2a41587f0 --- /dev/null +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/StringSourceTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.InputStream; +import java.util.Scanner; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class StringSourceTest { + @Test + void testGetInputStream() throws Exception { + StringSource source = new StringSource("Hello World!"); + + try (InputStream is = source.getInputStream(); + Scanner scanner = new Scanner(is)) { + assertEquals("Hello World!", scanner.nextLine()); + } + } + + @Test + void testGetLocation() { + StringSource source = new StringSource("Hello World!"); + assertEquals("(memory)", source.getLocation()); + + source = new StringSource("Hello World!", "LOCATION"); + assertEquals("LOCATION", source.getLocation()); + } + + @Test + void testGetContent() { + StringSource source = new StringSource(null); + assertEquals("", source.getContent()); + + source = new StringSource("Hello World!", "LOCATION"); + assertEquals("Hello World!", source.getContent()); + } +} diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/UrlSourceTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/UrlSourceTest.java new file mode 100644 index 000000000000..216c5d7c72a4 --- /dev/null +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/UrlSourceTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.building; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Scanner; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class UrlSourceTest { + + @Test + void testUrlSource() { + NullPointerException e = assertThrows( + NullPointerException.class, () -> new UrlSource(null), "Should fail, since you must specify a url"); + assertEquals("url cannot be null", e.getMessage()); + } + + @Test + void testGetInputStream() throws Exception { + URL txtFile = new File("target/test-classes/source.txt").toURI().toURL(); + UrlSource source = new UrlSource(txtFile); + try (InputStream is = source.getInputStream(); + Scanner scanner = new Scanner(is)) { + assertEquals("Hello World!", scanner.nextLine()); + } + } + + @Test + void testGetLocation() throws Exception { + URL txtFile = new File("target/test-classes/source.txt").toURI().toURL(); + UrlSource source = new UrlSource(txtFile); + assertEquals(txtFile.toExternalForm(), source.getLocation()); + } +} diff --git a/maven-builder-support/src/test/resources/source.txt b/compat/maven-builder-support/src/test/resources/source.txt similarity index 100% rename from maven-builder-support/src/test/resources/source.txt rename to compat/maven-builder-support/src/test/resources/source.txt diff --git a/compat/maven-compat/pom.xml b/compat/maven-compat/pom.xml new file mode 100644 index 000000000000..f6919bb5306e --- /dev/null +++ b/compat/maven-compat/pom.xml @@ -0,0 +1,255 @@ + + + + 4.0.0 + + + org.apache.maven + maven-compat-modules + 4.1.0-SNAPSHOT + + + maven-compat + + Maven Compat (deprecated) + Deprecated Maven2 classes maintained as compatibility layer. + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-core + + + org.apache.maven + maven-api-di + + + org.apache.maven + maven-api-metadata + + + org.apache.maven + maven-api-model + + + org.apache.maven + maven-api-toolchain + + + + org.apache.maven + maven-impl + + + org.apache.maven + maven-core + + + + org.apache.maven + maven-artifact + + + org.apache.maven + maven-model + + + org.apache.maven + maven-model-builder + + + org.apache.maven + maven-settings + + + org.apache.maven + maven-settings-builder + + + org.apache.maven + maven-plugin-api + + + org.apache.maven + maven-repository-metadata + + + org.apache.maven + maven-toolchain-builder + + + org.apache.maven + maven-toolchain-model + + + org.apache.maven + maven-resolver-provider + + + + org.apache.maven.resolver + maven-resolver-api + + + org.apache.maven.resolver + maven-resolver-util + + + + org.codehaus.plexus + plexus-classworlds + + + org.codehaus.plexus + plexus-interactivity-api + + + org.codehaus.plexus + plexus-interpolation + + + org.apache.maven.wagon + wagon-provider-api + + + + org.slf4j + slf4j-api + + + + org.codehaus.plexus + plexus-utils + + + org.codehaus.plexus + plexus-xml + + + + javax.inject + javax.inject + provided + + + org.eclipse.sisu + org.eclipse.sisu.inject + provided + + + + org.codehaus.plexus + plexus-component-annotations + 2.1.0 + + + org.eclipse.sisu + org.eclipse.sisu.plexus + provided + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest + test + + + com.google.inject + guice + classes + test + + + org.codehaus.plexus + plexus-testing + test + + + org.mockito + mockito-core + test + + + org.apache.maven.wagon + wagon-file + test + + + org.apache.maven.resolver + maven-resolver-spi + test + + + org.apache.maven.resolver + maven-resolver-impl + test + + + org.apache.maven.resolver + maven-resolver-connector-basic + test + + + org.apache.maven.resolver + maven-resolver-transport-wagon + test + + + + + + + org.eclipse.sisu + sisu-maven-plugin + + + org.codehaus.modello + modello-maven-plugin + + 1.0.0 + + src/main/mdo/profiles.mdo + src/main/mdo/paramdoc.mdo + + + + + modello + + java + xpp3-reader + xpp3-writer + + + + + + + + diff --git a/maven-core/src/main/java/org/apache/maven/ArtifactFilterManager.java b/compat/maven-compat/src/main/java/org/apache/maven/ArtifactFilterManager.java similarity index 83% rename from maven-core/src/main/java/org/apache/maven/ArtifactFilterManager.java rename to compat/maven-compat/src/main/java/org/apache/maven/ArtifactFilterManager.java index e15037e40208..bb101cce0be8 100644 --- a/maven-core/src/main/java/org/apache/maven/ArtifactFilterManager.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/ArtifactFilterManager.java @@ -1,5 +1,3 @@ -package org.apache.maven; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven; import java.util.Set; @@ -26,29 +25,34 @@ /** * ArtifactFilterManager */ -public interface ArtifactFilterManager -{ +@Deprecated +public interface ArtifactFilterManager { /** * Returns a filter for core + extension artifacts. - * + * + * @return the artifact filter * @deprecated use {@code META-INF/maven/extension.xml} to define artifacts exported by Maven core and plugin * extensions. */ + @Deprecated ArtifactFilter getArtifactFilter(); /** * Returns a filter for only the core artifacts. + * + * @return the artifact filter */ ArtifactFilter getCoreArtifactFilter(); /** * Exclude an extension artifact (doesn't affect getArtifactFilter's result, only getExtensionArtifactFilter). - * + * + * @param artifactId an artifact id * @deprecated use {@code META-INF/maven/extension.xml} to define artifacts exported by Maven core and plugin * extensions. */ - void excludeArtifact( String artifactId ); + @Deprecated + void excludeArtifact(String artifactId); Set getCoreArtifactExcludes(); - } diff --git a/maven-core/src/main/java/org/apache/maven/ArtifactFilterManagerDelegate.java b/compat/maven-compat/src/main/java/org/apache/maven/ArtifactFilterManagerDelegate.java similarity index 82% rename from maven-core/src/main/java/org/apache/maven/ArtifactFilterManagerDelegate.java rename to compat/maven-compat/src/main/java/org/apache/maven/ArtifactFilterManagerDelegate.java index 1389325482a1..c9e752e453fd 100644 --- a/maven-core/src/main/java/org/apache/maven/ArtifactFilterManagerDelegate.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/ArtifactFilterManagerDelegate.java @@ -1,5 +1,3 @@ -package org.apache.maven; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,17 +16,17 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven; import java.util.Set; /** * @deprecated use {@code META-INF/maven/extension.xml} to define artifacts exported by Maven core extensions. */ -public interface ArtifactFilterManagerDelegate -{ - - void addExcludes( Set excludes ); +@Deprecated +public interface ArtifactFilterManagerDelegate { - void addCoreExcludes( Set excludes ); + void addExcludes(Set excludes); + void addCoreExcludes(Set excludes); } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java b/compat/maven-compat/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java new file mode 100644 index 000000000000..c7f6590d57a0 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter; +import org.apache.maven.extension.internal.CoreExports; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultArtifactFilterManager implements ArtifactFilterManager { + + // this is a live injected collection + protected final List delegates; + + protected Set excludedArtifacts; + + private final Set coreArtifacts; + + @Inject + public DefaultArtifactFilterManager(List delegates, CoreExports coreExports) { + this.delegates = delegates; + this.coreArtifacts = coreExports.getExportedArtifacts(); + } + + private synchronized Set getExcludedArtifacts() { + if (excludedArtifacts == null) { + excludedArtifacts = new LinkedHashSet<>(coreArtifacts); + } + return excludedArtifacts; + } + + /** + * Returns the artifact filter for the core + extension artifacts. + * + * @see org.apache.maven.ArtifactFilterManager#getArtifactFilter() + */ + @Override + public ArtifactFilter getArtifactFilter() { + Set excludes = new LinkedHashSet<>(getExcludedArtifacts()); + + for (ArtifactFilterManagerDelegate delegate : delegates) { + delegate.addExcludes(excludes); + } + + return new ExclusionSetFilter(excludes); + } + + /** + * Returns the artifact filter for the standard core artifacts. + * + * @see org.apache.maven.ArtifactFilterManager#getCoreArtifactFilter() + */ + @Override + public ArtifactFilter getCoreArtifactFilter() { + return new ExclusionSetFilter(getCoreArtifactExcludes()); + } + + @Override + public void excludeArtifact(String artifactId) { + getExcludedArtifacts().add(artifactId); + } + + @Override + public Set getCoreArtifactExcludes() { + Set excludes = new LinkedHashSet<>(coreArtifacts); + + for (ArtifactFilterManagerDelegate delegate : delegates) { + delegate.addCoreExcludes(excludes); + } + + return excludes; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/DefaultProjectDependenciesResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/DefaultProjectDependenciesResolver.java new file mode 100644 index 000000000000..08d80f5f4e3f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/DefaultProjectDependenciesResolver.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException; +import org.apache.maven.artifact.resolver.ResolutionErrorHandler; +import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.artifact.ProjectArtifact; +import org.apache.maven.repository.RepositorySystem; + +/** + * @deprecated As of 3.2.2, and there is no direct replacement. This is an internal class which was not marked as such, + * but should have been. + * + */ +@Named +@Singleton +@Deprecated +public class DefaultProjectDependenciesResolver implements ProjectDependenciesResolver { + + private final RepositorySystem repositorySystem; + + private final ResolutionErrorHandler resolutionErrorHandler; + + @Inject + public DefaultProjectDependenciesResolver( + RepositorySystem repositorySystem, ResolutionErrorHandler resolutionErrorHandler) { + this.repositorySystem = repositorySystem; + this.resolutionErrorHandler = resolutionErrorHandler; + } + + @Override + public Set resolve(MavenProject project, Collection scopesToResolve, MavenSession session) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolve(Collections.singleton(project), scopesToResolve, session); + } + + @Override + public Set resolve( + MavenProject project, + Collection scopesToCollect, + Collection scopesToResolve, + MavenSession session) + throws ArtifactResolutionException, ArtifactNotFoundException { + Set mavenProjects = Collections.singleton(project); + return resolveImpl( + mavenProjects, scopesToCollect, scopesToResolve, session, getIgnorableArtifacts(mavenProjects)); + } + + @Override + public Set resolve( + Collection projects, Collection scopesToResolve, MavenSession session) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveImpl(projects, null, scopesToResolve, session, getIgnorableArtifacts(projects)); + } + + @Override + public Set resolve( + MavenProject project, + Collection scopesToCollect, + Collection scopesToResolve, + MavenSession session, + Set ignorableArtifacts) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveImpl( + Collections.singleton(project), + scopesToCollect, + scopesToResolve, + session, + getIgnorableArtifacts(ignorableArtifacts)); + } + + private Set resolveImpl( + Collection projects, + Collection scopesToCollect, + Collection scopesToResolve, + MavenSession session, + Set projectIds) + throws ArtifactResolutionException, ArtifactNotFoundException { + Set resolved = new LinkedHashSet<>(); + + if (projects == null || projects.isEmpty()) { + return resolved; + } + + if ((scopesToCollect == null || scopesToCollect.isEmpty()) + && (scopesToResolve == null || scopesToResolve.isEmpty())) { + return resolved; + } + + /* + + Logic for transitive global exclusions + + List exclusions = new ArrayList(); + + for ( Dependency d : project.getDependencies() ) + { + if ( d.getExclusions() != null ) + { + for ( Exclusion e : d.getExclusions() ) + { + exclusions.add( e.getGroupId() + ":" + e.getArtifactId() ); + } + } + } + + ArtifactFilter scopeFilter = new ScopeArtifactFilter( scope ); + + ArtifactFilter filter; + + if ( ! exclusions.isEmpty() ) + { + filter = new AndArtifactFilter( Arrays.asList( new ArtifactFilter[]{ + new ExcludesArtifactFilter( exclusions ), scopeFilter } ) ); + } + else + { + filter = scopeFilter; + } + */ + + CumulativeScopeArtifactFilter resolutionScopeFilter = new CumulativeScopeArtifactFilter(scopesToResolve); + + CumulativeScopeArtifactFilter collectionScopeFilter = new CumulativeScopeArtifactFilter(scopesToCollect); + collectionScopeFilter = new CumulativeScopeArtifactFilter(collectionScopeFilter, resolutionScopeFilter); + + ArtifactResolutionRequest request = new ArtifactResolutionRequest() + .setResolveRoot(false) + .setResolveTransitively(true) + .setCollectionFilter(collectionScopeFilter) + .setResolutionFilter(resolutionScopeFilter) + .setLocalRepository(session.getLocalRepository()) + .setOffline(session.isOffline()) + .setForceUpdate(session.getRequest().isUpdateSnapshots()); + request.setServers(session.getRequest().getServers()); + request.setMirrors(session.getRequest().getMirrors()); + request.setProxies(session.getRequest().getProxies()); + + for (MavenProject project : projects) { + request.setArtifact(new ProjectArtifact(project)); + request.setArtifactDependencies(project.getDependencyArtifacts()); + request.setManagedVersionMap(project.getManagedVersionMap()); + request.setRemoteRepositories(project.getRemoteArtifactRepositories()); + + ArtifactResolutionResult result = repositorySystem.resolve(request); + + try { + resolutionErrorHandler.throwErrors(request, result); + } catch (MultipleArtifactsNotFoundException e) { + + Collection missing = new HashSet<>(e.getMissingArtifacts()); + + for (Iterator it = missing.iterator(); it.hasNext(); ) { + String key = ArtifactUtils.key(it.next()); + if (projectIds.contains(key)) { + it.remove(); + } + } + + if (!missing.isEmpty()) { + throw e; + } + } + + resolved.addAll(result.getArtifacts()); + } + + return resolved; + } + + private Set getIgnorableArtifacts(Collection projects) { + Set projectIds = new HashSet<>(projects.size() * 2); + + for (MavenProject p : projects) { + String key = ArtifactUtils.key(p.getGroupId(), p.getArtifactId(), p.getVersion()); + projectIds.add(key); + } + return projectIds; + } + + private Set getIgnorableArtifacts(Iterable artifactIterable) { + Set projectIds = new HashSet<>(); + + for (Artifact artifact : artifactIterable) { + String key = ArtifactUtils.key(artifact); + projectIds.add(key); + } + return projectIds; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/ProjectDependenciesResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/ProjectDependenciesResolver.java new file mode 100644 index 000000000000..509ee5e9ed93 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/ProjectDependenciesResolver.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven; + +import java.util.Collection; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; + +/** + * @deprecated As of 3.2.2, and there is no direct replacement. This is an internal class which was not marked as such, + * but should have been. + * + */ +@Deprecated +public interface ProjectDependenciesResolver { + + /** + * Resolves the transitive dependencies of the specified project. + * + * @param project The project whose dependencies should be resolved, must not be {@code null}. + * @param scopesToResolve The dependency scopes that should be resolved, may be {@code null}. + * @param session The current build session, must not be {@code null}. + * @return The transitive dependencies of the specified project that match the requested scopes, never {@code null}. + * @throws ArtifactResolutionException in case of resolution issue + * @throws ArtifactNotFoundException if an artifact is not found + */ + Set resolve(MavenProject project, Collection scopesToResolve, MavenSession session) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Resolves the transitive dependencies of the specified project. + * + * @param project The project whose dependencies should be resolved, must not be {@code null}. + * @param scopesToCollect The dependency scopes that should be collected, may be {@code null}. + * @param scopesToResolve The dependency scopes that should be collected and also resolved, may be {@code null}. + * @param session The current build session, must not be {@code null}. + * @return The transitive dependencies of the specified project that match the requested scopes, never {@code null}. + * @throws ArtifactResolutionException in case of resolution issue + * @throws ArtifactNotFoundException if an artifact is not found + */ + Set resolve( + MavenProject project, + Collection scopesToCollect, + Collection scopesToResolve, + MavenSession session) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Resolves the transitive dependencies of the specified project. + * + * @param project The project whose dependencies should be resolved, must not be {@code null}. + * @param scopesToCollect The dependency scopes that should be collected, may be {@code null}. + * @param scopesToResolve The dependency scopes that should be collected and also resolved, may be {@code null}. + * @param session The current build session, must not be {@code null}. + * @param ignorableArtifacts Artifacts that need not be resolved + * @return The transitive dependencies of the specified project that match the requested scopes, never {@code null}. + * @throws ArtifactResolutionException in case of resolution issue + * @throws ArtifactNotFoundException if an artifact is not found + */ + Set resolve( + MavenProject project, + Collection scopesToCollect, + Collection scopesToResolve, + MavenSession session, + Set ignorableArtifacts) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Resolves the transitive dependencies of the specified projects. Note that dependencies which can't be resolved + * from any repository but are present among the set of specified projects will not cause an exception. Instead, + * those unresolved artifacts will be returned in the result set, allowing the caller to take special care of + * artifacts that haven't been build yet. + * + * @param projects The projects whose dependencies should be resolved, may be {@code null}. + * @param scopes The dependency scopes that should be resolved, may be {@code null}. + * @param session The current build session, must not be {@code null}. + * @return The transitive dependencies of the specified projects that match the requested scopes, never + * {@code null}. + * @throws ArtifactResolutionException in case of resolution issue + * @throws ArtifactNotFoundException if an artifact is not found + */ + Set resolve(Collection projects, Collection scopes, MavenSession session) + throws ArtifactResolutionException, ArtifactNotFoundException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/ArtifactScopeEnum.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/ArtifactScopeEnum.java new file mode 100644 index 000000000000..34178d5bc4ff --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/ArtifactScopeEnum.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +/** + * Type safe reincarnation of Artifact scope. Also supplies the {@code DEFAULT_SCOPE} as well + * as convenience method to deal with scope relationships. + * + * + */ +@Deprecated +public enum ArtifactScopeEnum { + compile(1), + test(2), + runtime(3), + provided(4), + system(5), + runtime_plus_system(6); + + public static final ArtifactScopeEnum DEFAULT_SCOPE = compile; + + private int id; + + // Constructor + ArtifactScopeEnum(int id) { + this.id = id; + } + + int getId() { + return id; + } + + /** + * Helper method to simplify null processing + * + * @param scope a scope or {@code null} + * @return the provided scope or DEFAULT_SCOPE + */ + public static ArtifactScopeEnum checkScope(ArtifactScopeEnum scope) { + return scope == null ? DEFAULT_SCOPE : scope; + } + + /** + * + * @return unsafe String representation of this scope. + */ + public String getScope() { + if (id == 1) { + return Artifact.SCOPE_COMPILE; + } else if (id == 2) { + return Artifact.SCOPE_TEST; + + } else if (id == 3) { + return Artifact.SCOPE_RUNTIME; + + } else if (id == 4) { + return Artifact.SCOPE_PROVIDED; + } else if (id == 5) { + return Artifact.SCOPE_SYSTEM; + } else { + return Artifact.SCOPE_RUNTIME_PLUS_SYSTEM; + } + } + + private static final ArtifactScopeEnum[][][] COMPLIANCY_SETS = { + {{compile}, {compile, provided, system}}, + {{test}, {compile, test, provided, system}}, + {{runtime}, {compile, runtime, system}}, + {{provided}, {compile, test, provided}} + }; + + /** + * scope relationship function. Used by the graph conflict resolution policies + * + * @param scope a scope + * @return true is supplied scope is an inclusive sub-scope of current one. + */ + public boolean encloses(ArtifactScopeEnum scope) { + final ArtifactScopeEnum s = checkScope(scope); + + // system scope is historic only - and simple + if (id == system.id) { + return scope.id == system.id; + } + + for (ArtifactScopeEnum[][] set : COMPLIANCY_SETS) { + if (id == set[0][0].id) { + for (ArtifactScopeEnum ase : set[1]) { + if (s.id == ase.id) { + return true; + } + } + break; + } + } + return false; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/ArtifactStatus.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/ArtifactStatus.java new file mode 100644 index 000000000000..a755cacada57 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/ArtifactStatus.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import java.util.HashMap; +import java.util.Map; + +/** + * Type safe enumeration for the artifact status field. + * + */ +@Deprecated +public final class ArtifactStatus implements Comparable { + /** + * No trust - no information about status. + */ + public static final ArtifactStatus NONE = new ArtifactStatus("none", 0); + + /** + * No trust - information was generated with defaults. + */ + public static final ArtifactStatus GENERATED = new ArtifactStatus("generated", 1); + + /** + * Low trust - was converted from the Maven 1.x repository. + */ + public static final ArtifactStatus CONVERTED = new ArtifactStatus("converted", 2); + + /** + * Moderate trust - it was deployed directly from a partner. + */ + public static final ArtifactStatus PARTNER = new ArtifactStatus("partner", 3); + + /** + * Moderate trust - it was deployed directly by a user. + */ + public static final ArtifactStatus DEPLOYED = new ArtifactStatus("deployed", 4); + + /** + * Trusted, as it has had its data verified by hand. + */ + public static final ArtifactStatus VERIFIED = new ArtifactStatus("verified", 5); + + private final int rank; + + private final String key; + + private static Map map; + + private ArtifactStatus(String key, int rank) { + this.rank = rank; + this.key = key; + + if (map == null) { + map = new HashMap<>(); + } + map.put(key, this); + } + + public static ArtifactStatus valueOf(String status) { + ArtifactStatus retVal = null; + + if (status != null) { + retVal = map.get(status); + } + + return retVal != null ? retVal : NONE; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final ArtifactStatus that = (ArtifactStatus) o; + + return rank == that.rank; + } + + @Override + public int hashCode() { + return rank; + } + + @Override + public String toString() { + return key; + } + + @Override + public int compareTo(ArtifactStatus s) { + return rank - s.rank; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/UnknownRepositoryLayoutException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/UnknownRepositoryLayoutException.java new file mode 100644 index 000000000000..dcaa874cb6dd --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/UnknownRepositoryLayoutException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +/** + * Exception which is meant to occur when a layout specified for a particular + * repository doesn't have a corresponding {@link org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout} + * component in the current container. + * + */ +@Deprecated +public class UnknownRepositoryLayoutException extends InvalidRepositoryException { + + private final String layoutId; + + public UnknownRepositoryLayoutException(String repositoryId, String layoutId) { + super("Cannot find ArtifactRepositoryLayout instance for: " + layoutId, repositoryId); + this.layoutId = layoutId; + } + + public UnknownRepositoryLayoutException(String repositoryId, String layoutId, ComponentLookupException e) { + super("Cannot find ArtifactRepositoryLayout instance for: " + layoutId, repositoryId, e); + this.layoutId = layoutId; + } + + public String getLayoutId() { + return layoutId; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/ArtifactDeployer.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/ArtifactDeployer.java new file mode 100644 index 000000000000..43dbd13ea591 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/ArtifactDeployer.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.deployer; + +import java.io.File; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * ArtifactDeployer + */ +@Deprecated +public interface ArtifactDeployer { + String ROLE = ArtifactDeployer.class.getName(); + + /** + * Deploy an artifact from a particular directory. The artifact handler is used to determine the + * filename of the source file. + * + * @param basedir the directory where the artifact is stored + * @param finalName the name of the artifact without extension + * @param artifact the artifact definition + * @param deploymentRepository the repository to deploy to + * @param localRepository the local repository to install into + * @throws ArtifactDeploymentException if an error occurred deploying the artifact + * @deprecated to be removed before 2.0 after the install/deploy plugins use the alternate + * method + */ + @Deprecated + void deploy( + String basedir, + String finalName, + Artifact artifact, + ArtifactRepository deploymentRepository, + ArtifactRepository localRepository) + throws ArtifactDeploymentException; + + /** + * Deploy an artifact from a particular file. + * + * @param source the file to deploy + * @param artifact the artifact definition + * @param deploymentRepository the repository to deploy to + * @param localRepository the local repository to install into + * @throws ArtifactDeploymentException if an error occurred deploying the artifact + */ + void deploy( + File source, Artifact artifact, ArtifactRepository deploymentRepository, ArtifactRepository localRepository) + throws ArtifactDeploymentException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/ArtifactDeploymentException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/ArtifactDeploymentException.java new file mode 100644 index 000000000000..6351873bfa12 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/ArtifactDeploymentException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.deployer; + +/** + */ +@Deprecated +public class ArtifactDeploymentException extends Exception { + public ArtifactDeploymentException(String message) { + super(message); + } + + public ArtifactDeploymentException(Throwable cause) { + super(cause); + } + + public ArtifactDeploymentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/DefaultArtifactDeployer.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/DefaultArtifactDeployer.java new file mode 100644 index 000000000000..b1443490482c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/deployer/DefaultArtifactDeployer.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.deployer; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultArtifactRepository; +import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager; +import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.MetadataBridge; +import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.project.artifact.ProjectArtifactMetadata; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.deployment.DeployRequest; +import org.eclipse.aether.deployment.DeployResult; +import org.eclipse.aether.deployment.DeploymentException; +import org.eclipse.aether.metadata.MergeableMetadata; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.artifact.SubArtifact; + +/** + * DefaultArtifactDeployer + */ +@Named +@Deprecated +public class DefaultArtifactDeployer extends AbstractLogEnabled implements ArtifactDeployer { + + @Inject + private RepositorySystem repoSystem; + + @Inject + private LegacySupport legacySupport; + + private Map relatedMetadata = new ConcurrentHashMap<>(); + + /** + * @deprecated we want to use the artifact method only, and ensure artifact.file is set + * correctly. + */ + @Deprecated + @Override + public void deploy( + String basedir, + String finalName, + Artifact artifact, + ArtifactRepository deploymentRepository, + ArtifactRepository localRepository) + throws ArtifactDeploymentException { + String extension = artifact.getArtifactHandler().getExtension(); + File source = new File(basedir, finalName + "." + extension); + deploy(source, artifact, deploymentRepository, localRepository); + } + + @Override + public void deploy( + File source, Artifact artifact, ArtifactRepository deploymentRepository, ArtifactRepository localRepository) + throws ArtifactDeploymentException { + RepositorySystemSession session = + LegacyLocalRepositoryManager.overlay(localRepository, legacySupport.getRepositorySession(), repoSystem); + + DeployRequest request = new DeployRequest(); + + request.setTrace(RequestTrace.newChild(null, legacySupport.getSession().getCurrentProject())); + + org.eclipse.aether.artifact.Artifact mainArtifact = RepositoryUtils.toArtifact(artifact); + mainArtifact = mainArtifact.setFile(source); + request.addArtifact(mainArtifact); + + String versionKey = artifact.getGroupId() + ':' + artifact.getArtifactId(); + String snapshotKey = null; + if (artifact.isSnapshot()) { + snapshotKey = versionKey + ':' + artifact.getBaseVersion(); + request.addMetadata(relatedMetadata.get(snapshotKey)); + } + request.addMetadata(relatedMetadata.get(versionKey)); + + for (ArtifactMetadata metadata : artifact.getMetadataList()) { + if (metadata instanceof ProjectArtifactMetadata projectArtifactMetadata) { + org.eclipse.aether.artifact.Artifact pomArtifact = new SubArtifact(mainArtifact, "", "pom"); + pomArtifact = pomArtifact.setFile(projectArtifactMetadata.getFile()); + request.addArtifact(pomArtifact); + } else if (metadata instanceof SnapshotArtifactRepositoryMetadata + || metadata instanceof ArtifactRepositoryMetadata) { + // eaten, handled by repo system + } else { + request.addMetadata(new MetadataBridge(metadata)); + } + } + + RemoteRepository remoteRepo = RepositoryUtils.toRepo(deploymentRepository); + /* + * NOTE: This provides backward-compat with maven-deploy-plugin:2.4 which bypasses the repository factory when + * using an alternative deployment location. + */ + if (deploymentRepository instanceof DefaultArtifactRepository + && deploymentRepository.getAuthentication() == null) { + RemoteRepository.Builder builder = new RemoteRepository.Builder(remoteRepo); + builder.setAuthentication(session.getAuthenticationSelector().getAuthentication(remoteRepo)); + builder.setProxy(session.getProxySelector().getProxy(remoteRepo)); + remoteRepo = builder.build(); + } + request.setRepository(remoteRepo); + + DeployResult result; + try { + result = repoSystem.deploy(session, request); + } catch (DeploymentException e) { + throw new ArtifactDeploymentException(e.getMessage(), e); + } + + for (Object metadata : result.getMetadata()) { + if (metadata.getClass().getName().endsWith(".internal.VersionsMetadata")) { + relatedMetadata.put(versionKey, (MergeableMetadata) metadata); + } + if (snapshotKey != null && metadata.getClass().getName().endsWith(".internal.RemoteSnapshotMetadata")) { + relatedMetadata.put(snapshotKey, (MergeableMetadata) metadata); + } + } + + artifact.setResolvedVersion(result.getArtifacts().iterator().next().getVersion()); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/ArtifactInstallationException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/ArtifactInstallationException.java new file mode 100644 index 000000000000..830a90bd11de --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/ArtifactInstallationException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.installer; + +/** + */ +@Deprecated +public class ArtifactInstallationException extends Exception { + public ArtifactInstallationException(String message) { + super(message); + } + + public ArtifactInstallationException(Throwable cause) { + super(cause); + } + + public ArtifactInstallationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/ArtifactInstaller.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/ArtifactInstaller.java new file mode 100644 index 000000000000..f9f4c12c771f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/ArtifactInstaller.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.installer; + +import java.io.File; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + */ +@Deprecated +public interface ArtifactInstaller { + String ROLE = ArtifactInstaller.class.getName(); + + /** + * Install an artifact from a particular directory. The artifact handler is used to determine + * the filename of the source file. + * + * @param basedir the directory where the artifact is stored + * @param finalName the name of the artifact sans extension + * @param artifact the artifact definition + * @param localRepository the local repository to install into + * @throws ArtifactInstallationException if an error occurred installing the artifact + * @deprecated to be removed before 2.0 after the install/deploy plugins use the alternate + * method + */ + @Deprecated + void install(String basedir, String finalName, Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException; + + /** + * Install an artifact from a particular file. + * + * @param source the file to install + * @param artifact the artifact definition + * @param localRepository the local repository to install into + * @throws ArtifactInstallationException if an error occurred installing the artifact + */ + void install(File source, Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/DefaultArtifactInstaller.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/DefaultArtifactInstaller.java new file mode 100644 index 000000000000..a343a9a7b891 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/installer/DefaultArtifactInstaller.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.installer; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager; +import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.MetadataBridge; +import org.apache.maven.artifact.repository.metadata.Snapshot; +import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.project.artifact.ProjectArtifactMetadata; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.installation.InstallRequest; +import org.eclipse.aether.installation.InstallationException; +import org.eclipse.aether.util.artifact.SubArtifact; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultArtifactInstaller extends AbstractLogEnabled implements ArtifactInstaller { + + @Inject + private RepositorySystem repoSystem; + + @Inject + private LegacySupport legacySupport; + + /** @deprecated we want to use the artifact method only, and ensure artifact.file is set correctly. */ + @Deprecated + @Override + public void install(String basedir, String finalName, Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException { + String extension = artifact.getArtifactHandler().getExtension(); + File source = new File(basedir, finalName + "." + extension); + + install(source, artifact, localRepository); + } + + @Override + public void install(File source, Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException { + RepositorySystemSession session = + LegacyLocalRepositoryManager.overlay(localRepository, legacySupport.getRepositorySession(), repoSystem); + + InstallRequest request = new InstallRequest(); + + request.setTrace(RequestTrace.newChild(null, legacySupport.getSession().getCurrentProject())); + + org.eclipse.aether.artifact.Artifact mainArtifact = RepositoryUtils.toArtifact(artifact); + mainArtifact = mainArtifact.setFile(source); + request.addArtifact(mainArtifact); + + for (ArtifactMetadata metadata : artifact.getMetadataList()) { + if (metadata instanceof ProjectArtifactMetadata projectArtifactMetadata) { + org.eclipse.aether.artifact.Artifact pomArtifact = new SubArtifact(mainArtifact, "", "pom"); + pomArtifact = pomArtifact.setFile(projectArtifactMetadata.getFile()); + request.addArtifact(pomArtifact); + } else if (metadata instanceof SnapshotArtifactRepositoryMetadata + || metadata instanceof ArtifactRepositoryMetadata) { + // eaten, handled by repo system + } else { + request.addMetadata(new MetadataBridge(metadata)); + } + } + + try { + repoSystem.install(session, request); + } catch (InstallationException e) { + throw new ArtifactInstallationException(e.getMessage(), e); + } + + /* + * NOTE: Not used by Maven core, only here to provide backward-compat with plugins like the Install Plugin. + */ + + if (artifact.isSnapshot()) { + Snapshot snapshot = new Snapshot(); + snapshot.setLocalCopy(true); + artifact.addMetadata(new SnapshotArtifactRepositoryMetadata(artifact, snapshot)); + } + + Versioning versioning = new Versioning(); + // TODO Should this be changed for MNG-6754 too? + versioning.updateTimestamp(); + versioning.addVersion(artifact.getBaseVersion()); + if (artifact.isRelease()) { + versioning.setRelease(artifact.getBaseVersion()); + } + artifact.addMetadata(new ArtifactRepositoryMetadata(artifact, versioning)); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/DefaultWagonManager.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/DefaultWagonManager.java new file mode 100644 index 000000000000..28b912868dec --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/DefaultWagonManager.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.manager; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.MirrorSelector; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Proxy; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecrypter; +import org.apache.maven.settings.crypto.SettingsDecryptionResult; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.authentication.AuthenticationInfo; +import org.apache.maven.wagon.proxy.ProxyInfo; +import org.codehaus.plexus.logging.Logger; + +/** + * Manages Wagon related operations in Maven. + */ +@Named +@Singleton +@Deprecated +public class DefaultWagonManager extends org.apache.maven.repository.legacy.DefaultWagonManager + implements WagonManager { + + // NOTE: This must use a different field name than in the super class or IoC has no chance to inject the loggers + @Inject + private Logger log; + + @Inject + private LegacySupport legacySupport; + + @Inject + private SettingsDecrypter settingsDecrypter; + + @Inject + private MirrorSelector mirrorSelector; + + @Inject + private ArtifactRepositoryFactory artifactRepositoryFactory; + + @Override + public AuthenticationInfo getAuthenticationInfo(String id) { + MavenSession session = legacySupport.getSession(); + + if (session != null && id != null) { + MavenExecutionRequest request = session.getRequest(); + + if (request != null) { + List servers = request.getServers(); + + if (servers != null) { + for (Server server : servers) { + if (id.equalsIgnoreCase(server.getId())) { + SettingsDecryptionResult result = + settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(server)); + server = result.getServer(); + + AuthenticationInfo authInfo = new AuthenticationInfo(); + authInfo.setUserName(server.getUsername()); + authInfo.setPassword(server.getPassword()); + authInfo.setPrivateKey(server.getPrivateKey()); + authInfo.setPassphrase(server.getPassphrase()); + + return authInfo; + } + } + } + } + } + + // empty one to prevent NPE + return new AuthenticationInfo(); + } + + @Override + public ProxyInfo getProxy(String protocol) { + MavenSession session = legacySupport.getSession(); + + if (session != null && protocol != null) { + MavenExecutionRequest request = session.getRequest(); + + if (request != null) { + List proxies = request.getProxies(); + + if (proxies != null) { + for (Proxy proxy : proxies) { + if (proxy.isActive() && protocol.equalsIgnoreCase(proxy.getProtocol())) { + SettingsDecryptionResult result = + settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(proxy)); + proxy = result.getProxy(); + + ProxyInfo proxyInfo = new ProxyInfo(); + proxyInfo.setHost(proxy.getHost()); + proxyInfo.setType(proxy.getProtocol()); + proxyInfo.setPort(proxy.getPort()); + proxyInfo.setNonProxyHosts(proxy.getNonProxyHosts()); + proxyInfo.setUserName(proxy.getUsername()); + proxyInfo.setPassword(proxy.getPassword()); + + return proxyInfo; + } + } + } + } + } + + return null; + } + + @Override + public void getArtifact(Artifact artifact, ArtifactRepository repository) + throws TransferFailedException, ResourceDoesNotExistException { + getArtifact(artifact, repository, null, false); + } + + @Override + public void getArtifact(Artifact artifact, List remoteRepositories) + throws TransferFailedException, ResourceDoesNotExistException { + getArtifact(artifact, remoteRepositories, null, false); + } + + @Deprecated + @Override + public ArtifactRepository getMirrorRepository(ArtifactRepository repository) { + + Mirror mirror = mirrorSelector.getMirror( + repository, legacySupport.getSession().getSettings().getMirrors()); + + if (mirror != null) { + String id = mirror.getId(); + if (id == null) { + // TODO this should be illegal in settings.xml + id = repository.getId(); + } + + log.debug("Using mirror: " + mirror.getUrl() + " (id: " + id + ")"); + + repository = artifactRepositoryFactory.createArtifactRepository( + id, mirror.getUrl(), repository.getLayout(), repository.getSnapshots(), repository.getReleases()); + } + return repository; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/WagonConfigurationException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/WagonConfigurationException.java new file mode 100644 index 000000000000..923ab3c68944 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/WagonConfigurationException.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.manager; + +/** + */ +@Deprecated +public class WagonConfigurationException extends org.apache.maven.repository.legacy.WagonConfigurationException { + public WagonConfigurationException(String repositoryId, String message, Throwable cause) { + super(repositoryId, message, cause); + } + + public WagonConfigurationException(String repositoryId, String message) { + super(repositoryId, message); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/WagonManager.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/WagonManager.java new file mode 100644 index 000000000000..8ae5e50dc864 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/manager/WagonManager.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.manager; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.authentication.AuthenticationInfo; +import org.apache.maven.wagon.proxy.ProxyInfo; + +/** + * Manages Wagon related operations in Maven. + * + */ +@Deprecated +public interface WagonManager extends org.apache.maven.repository.legacy.WagonManager { + /** + * this method is only here for backward compat (project-info-reports:dependencies) + * the default implementation will return an empty AuthenticationInfo + * + * @param id an id + * @return corresponding authentication info + */ + AuthenticationInfo getAuthenticationInfo(String id); + + ProxyInfo getProxy(String protocol); + + void getArtifact(Artifact artifact, ArtifactRepository repository) + throws TransferFailedException, ResourceDoesNotExistException; + + void getArtifact(Artifact artifact, List remoteRepositories) + throws TransferFailedException, ResourceDoesNotExistException; + + ArtifactRepository getMirrorRepository(ArtifactRepository repository); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/AbstractArtifactMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/AbstractArtifactMetadata.java new file mode 100644 index 000000000000..1562d19c1ba3 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/AbstractArtifactMetadata.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +import org.apache.maven.artifact.Artifact; + +/** + * AbstractArtifactMetadata + */ +@Deprecated +public abstract class AbstractArtifactMetadata + extends org.apache.maven.repository.legacy.metadata.AbstractArtifactMetadata + implements org.apache.maven.artifact.metadata.ArtifactMetadata { + protected AbstractArtifactMetadata(Artifact artifact) { + super(artifact); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadataRetrievalException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadataRetrievalException.java new file mode 100644 index 000000000000..5330f7e3fc6b --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadataRetrievalException.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +import org.apache.maven.artifact.Artifact; + +/** + * Error while retrieving repository metadata from the repository - deprecated + */ +@Deprecated +public class ArtifactMetadataRetrievalException + extends org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException { + + /** + * @param message a message + * @deprecated use {@link #ArtifactMetadataRetrievalException(String, Throwable, Artifact)} + */ + @Deprecated + public ArtifactMetadataRetrievalException(String message) { + super(message, null, null); + } + + /** + * @param cause a cause + * @deprecated use {@link #ArtifactMetadataRetrievalException(String, Throwable, Artifact)} + */ + @Deprecated + public ArtifactMetadataRetrievalException(Throwable cause) { + super(null, cause, null); + } + + /** + * @param message a message + * @param cause a cause + * @deprecated use {@link #ArtifactMetadataRetrievalException(String, Throwable, Artifact)} + */ + @Deprecated + public ArtifactMetadataRetrievalException(String message, Throwable cause) { + super(message, cause, null); + } + + public ArtifactMetadataRetrievalException(String message, Throwable cause, Artifact artifact) { + super(message, cause, artifact); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadataSource.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadataSource.java new file mode 100644 index 000000000000..f916588ea347 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ArtifactMetadataSource.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; + +/** + * Provides some metadata operations, like querying the remote repository for a list of versions available for an + * artifact - deprecated + */ +@Deprecated +public interface ArtifactMetadataSource extends org.apache.maven.repository.legacy.metadata.ArtifactMetadataSource { + + @Override + ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException; + + @Override + ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException; + + List retrieveAvailableVersions(MetadataResolutionRequest request) + throws ArtifactMetadataRetrievalException; + + @Override + List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException; + + @Override + List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws ArtifactMetadataRetrievalException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ResolutionGroup.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ResolutionGroup.java new file mode 100644 index 000000000000..25b5b657e463 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/metadata/ResolutionGroup.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * ResolutionGroup + */ +@Deprecated +public class ResolutionGroup extends org.apache.maven.repository.legacy.metadata.ResolutionGroup { + + public ResolutionGroup( + Artifact pomArtifact, Set artifacts, List resolutionRepositories) { + super(pomArtifact, artifacts, resolutionRepositories); + } + + public ResolutionGroup( + Artifact pomArtifact, + Artifact relocatedArtifact, + Set artifacts, + Map managedVersions, + List resolutionRepositories) { + super(pomArtifact, relocatedArtifact, artifacts, managedVersions, resolutionRepositories); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/ArtifactRepositoryFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/ArtifactRepositoryFactory.java new file mode 100644 index 000000000000..85a2f24c9182 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/ArtifactRepositoryFactory.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import org.apache.maven.artifact.UnknownRepositoryLayoutException; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; + +/** + */ +@Deprecated +public interface ArtifactRepositoryFactory { + String ROLE = ArtifactRepositoryFactory.class.getName(); + + String DEFAULT_LAYOUT_ID = "default"; + + String LOCAL_REPOSITORY_ID = "local"; + + @Deprecated + ArtifactRepositoryLayout getLayout(String layoutId) throws UnknownRepositoryLayoutException; + + @Deprecated + ArtifactRepository createDeploymentArtifactRepository(String id, String url, String layoutId, boolean uniqueVersion) + throws UnknownRepositoryLayoutException; + + ArtifactRepository createDeploymentArtifactRepository( + String id, String url, ArtifactRepositoryLayout layout, boolean uniqueVersion); + + ArtifactRepository createArtifactRepository( + String id, + String url, + String layoutId, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) + throws UnknownRepositoryLayoutException; + + ArtifactRepository createArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases); + + void setGlobalUpdatePolicy(String snapshotPolicy); + + void setGlobalChecksumPolicy(String checksumPolicy); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/DefaultArtifactRepository.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/DefaultArtifactRepository.java new file mode 100644 index 000000000000..c59cd2b5c6fe --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/DefaultArtifactRepository.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.repository.Proxy; +import org.apache.maven.wagon.repository.Repository; + +/** + * This class is an abstraction of the location from/to resources can be + * transferred. + * + */ +@Deprecated +public class DefaultArtifactRepository extends Repository implements ArtifactRepository { + private ArtifactRepositoryLayout layout; + + private ArtifactRepositoryPolicy snapshots; + + private ArtifactRepositoryPolicy releases; + + private boolean blacklisted; + + private Authentication authentication; + + private Proxy proxy; + + private List mirroredRepositories = Collections.emptyList(); + + private boolean blocked; + + /** + * Create a local repository or a test repository. + * + * @param id the unique identifier of the repository + * @param url the URL of the repository + * @param layout the layout of the repository + */ + public DefaultArtifactRepository(String id, String url, ArtifactRepositoryLayout layout) { + this(id, url, layout, null, null); + } + + /** + * Create a remote deployment repository. + * + * @param id the unique identifier of the repository + * @param url the URL of the repository + * @param layout the layout of the repository + * @param uniqueVersion whether to assign each snapshot a unique version + */ + public DefaultArtifactRepository(String id, String url, ArtifactRepositoryLayout layout, boolean uniqueVersion) { + super(id, url); + this.layout = layout; + } + + /** + * Create a remote download repository. + * + * @param id the unique identifier of the repository + * @param url the URL of the repository + * @param layout the layout of the repository + * @param snapshots the policies to use for snapshots + * @param releases the policies to use for releases + */ + public DefaultArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout layout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) { + super(id, url); + + this.layout = layout; + + if (snapshots == null) { + snapshots = new ArtifactRepositoryPolicy( + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, + ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE); + } + + this.snapshots = snapshots; + + if (releases == null) { + releases = new ArtifactRepositoryPolicy( + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, + ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE); + } + + this.releases = releases; + } + + @Override + public String pathOf(Artifact artifact) { + return layout.pathOf(artifact); + } + + @Override + public String pathOfRemoteRepositoryMetadata(ArtifactMetadata artifactMetadata) { + return layout.pathOfRemoteRepositoryMetadata(artifactMetadata); + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return layout.pathOfLocalRepositoryMetadata(metadata, repository); + } + + @Override + public void setLayout(ArtifactRepositoryLayout layout) { + this.layout = layout; + } + + @Override + public ArtifactRepositoryLayout getLayout() { + return layout; + } + + @Override + public void setSnapshotUpdatePolicy(ArtifactRepositoryPolicy snapshots) { + this.snapshots = snapshots; + } + + @Override + public ArtifactRepositoryPolicy getSnapshots() { + return snapshots; + } + + @Override + public void setReleaseUpdatePolicy(ArtifactRepositoryPolicy releases) { + this.releases = releases; + } + + @Override + public ArtifactRepositoryPolicy getReleases() { + return releases; + } + + @Override + public String getKey() { + return getId(); + } + + @Override + public boolean isBlacklisted() { + return blacklisted; + } + + @Override + public void setBlacklisted(boolean blacklisted) { + this.blacklisted = blacklisted; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(256); + + sb.append(" id: ").append(getId()).append('\n'); + sb.append(" url: ").append(getUrl()).append('\n'); + sb.append(" layout: ").append(layout != null ? layout : "none").append('\n'); + + if (snapshots != null) { + sb.append("snapshots: [enabled => ").append(snapshots.isEnabled()); + sb.append(", update => ").append(snapshots.getUpdatePolicy()).append("]\n"); + } + + if (releases != null) { + sb.append(" releases: [enabled => ").append(releases.isEnabled()); + sb.append(", update => ").append(releases.getUpdatePolicy()).append("]\n"); + } + + return sb.toString(); + } + + @Override + public Artifact find(Artifact artifact) { + File artifactFile = new File(getBasedir(), pathOf(artifact)); + + // We need to set the file here or the resolver will fail with an NPE, not fully equipped to deal + // with multiple local repository implementations yet. + artifact.setFile(artifactFile); + + if (artifactFile.exists()) { + artifact.setResolved(true); + } + + return artifact; + } + + @Override + public List findVersions(Artifact artifact) { + return Collections.emptyList(); + } + + @Override + public boolean isProjectAware() { + return false; + } + + @Override + public Authentication getAuthentication() { + return authentication; + } + + @Override + public void setAuthentication(Authentication authentication) { + this.authentication = authentication; + } + + @Override + public Proxy getProxy() { + return proxy; + } + + @Override + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } + + @Override + public boolean isUniqueVersion() { + return true; + } + + @Override + public List getMirroredRepositories() { + return mirroredRepositories; + } + + @Override + public void setMirroredRepositories(List mirroredRepositories) { + if (mirroredRepositories != null) { + this.mirroredRepositories = Collections.unmodifiableList(mirroredRepositories); + } else { + this.mirroredRepositories = Collections.emptyList(); + } + } + + @Override + public boolean isBlocked() { + return blocked; + } + + @Override + public void setBlocked(boolean blocked) { + this.blocked = blocked; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/DefaultArtifactRepositoryFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/DefaultArtifactRepositoryFactory.java new file mode 100644 index 000000000000..ce348b87c966 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/DefaultArtifactRepositoryFactory.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.artifact.UnknownRepositoryLayoutException; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.RepositorySystem; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.eclipse.aether.RepositorySystemSession; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultArtifactRepositoryFactory implements ArtifactRepositoryFactory { + + @Inject + private org.apache.maven.repository.legacy.repository.ArtifactRepositoryFactory factory; + + @Inject + private LegacySupport legacySupport; + + @Inject + private PlexusContainer container; + + @Override + public ArtifactRepositoryLayout getLayout(String layoutId) throws UnknownRepositoryLayoutException { + return factory.getLayout(layoutId); + } + + @Override + public ArtifactRepository createDeploymentArtifactRepository( + String id, String url, String layoutId, boolean uniqueVersion) throws UnknownRepositoryLayoutException { + return injectSession(factory.createDeploymentArtifactRepository(id, url, layoutId, uniqueVersion), false); + } + + @Override + public ArtifactRepository createDeploymentArtifactRepository( + String id, String url, ArtifactRepositoryLayout repositoryLayout, boolean uniqueVersion) { + return injectSession( + factory.createDeploymentArtifactRepository(id, url, repositoryLayout, uniqueVersion), false); + } + + @Override + public ArtifactRepository createArtifactRepository( + String id, + String url, + String layoutId, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) + throws UnknownRepositoryLayoutException { + return injectSession(factory.createArtifactRepository(id, url, layoutId, snapshots, releases), true); + } + + @Override + public ArtifactRepository createArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) { + return injectSession(factory.createArtifactRepository(id, url, repositoryLayout, snapshots, releases), true); + } + + @Override + public void setGlobalUpdatePolicy(String updatePolicy) { + factory.setGlobalUpdatePolicy(updatePolicy); + } + + @Override + public void setGlobalChecksumPolicy(String checksumPolicy) { + factory.setGlobalChecksumPolicy(checksumPolicy); + } + + private ArtifactRepository injectSession(ArtifactRepository repository, boolean mirrors) { + RepositorySystemSession session = legacySupport.getRepositorySession(); + + if (session != null && repository != null && !isLocalRepository(repository)) { + List repositories = Arrays.asList(repository); + + RepositorySystem repositorySystem; + try { + repositorySystem = container.lookup(RepositorySystem.class); + } catch (ComponentLookupException e) { + throw new IllegalStateException("Unable to lookup " + RepositorySystem.class.getName()); + } + + if (mirrors) { + repositorySystem.injectMirror(session, repositories); + } + + repositorySystem.injectProxy(session, repositories); + + repositorySystem.injectAuthentication(session, repositories); + } + + return repository; + } + + private boolean isLocalRepository(ArtifactRepository repository) { + // unfortunately, the API doesn't allow to tell a remote repo and the local repo apart... + return "local".equals(repository.getId()); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/LegacyLocalRepositoryManager.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/LegacyLocalRepositoryManager.java new file mode 100644 index 000000000000..0d36d42f1f8c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/LegacyLocalRepositoryManager.java @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataStoreException; +import org.apache.maven.repository.Proxy; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.LocalArtifactRegistration; +import org.eclipse.aether.repository.LocalArtifactRequest; +import org.eclipse.aether.repository.LocalArtifactResult; +import org.eclipse.aether.repository.LocalMetadataRegistration; +import org.eclipse.aether.repository.LocalMetadataRequest; +import org.eclipse.aether.repository.LocalMetadataResult; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.LocalRepositoryManager; +import org.eclipse.aether.repository.RemoteRepository; + +/** + * Warning: This is an internal utility class that is only public for technical reasons, it is not part + * of the public API. In particular, this class can be changed or deleted without prior notice. + * + */ +@Deprecated +public class LegacyLocalRepositoryManager implements LocalRepositoryManager { + + private final ArtifactRepository delegate; + + private final LocalRepository repo; + + private final boolean realLocalRepo; + + public static RepositorySystemSession overlay( + ArtifactRepository repository, RepositorySystemSession session, RepositorySystem system) { + return overlay(repository, session); + } + + public static RepositorySystemSession overlay(ArtifactRepository repository, RepositorySystemSession session) { + if (repository == null || repository.getBasedir() == null) { + return session; + } + + if (session != null) { + LocalRepositoryManager lrm = session.getLocalRepositoryManager(); + if (lrm != null && lrm.getRepository().getBasedir().equals(new File(repository.getBasedir()))) { + return session; + } + } else { + session = new DefaultRepositorySystemSession(); + } + + final LocalRepositoryManager llrm = new LegacyLocalRepositoryManager(repository); + + return new DefaultRepositorySystemSession(session).setLocalRepositoryManager(llrm); + } + + private LegacyLocalRepositoryManager(ArtifactRepository delegate) { + this.delegate = Objects.requireNonNull(delegate, "delegate cannot be null"); + + ArtifactRepositoryLayout layout = delegate.getLayout(); + repo = new LocalRepository( + new File(delegate.getBasedir()), + (layout != null) ? layout.getClass().getSimpleName() : "legacy"); + + /* + * NOTE: "invoker:install" vs "appassembler:assemble": Both mojos use the artifact installer to put an artifact + * into a repository. In the first case, the result needs to be a proper local repository that one can use for + * local artifact resolution. In the second case, the result needs to precisely obey the path information of the + * repository's layout to allow pointing at artifacts within the repository. Unfortunately, + * DefaultRepositoryLayout does not correctly describe the layout of a local repository which unlike a remote + * repository never uses timestamps in the filename of a snapshot artifact. The discrepancy gets notable when a + * remotely resolved snapshot artifact gets passed into pathOf(). So producing a proper local artifact path + * using DefaultRepositoryLayout requires us to enforce usage of the artifact's base version. This + * transformation however contradicts the other use case of precisely obeying the repository's layout. The below + * flag tries to detect which use case applies to make both plugins happy. + */ + realLocalRepo = (layout instanceof DefaultRepositoryLayout) && "local".equals(delegate.getId()); + } + + @Override + public LocalRepository getRepository() { + return repo; + } + + @Override + public String getPathForLocalArtifact(Artifact artifact) { + if (realLocalRepo) { + return delegate.pathOf(RepositoryUtils.toArtifact(artifact.setVersion(artifact.getBaseVersion()))); + } + return delegate.pathOf(RepositoryUtils.toArtifact(artifact)); + } + + @Override + public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) { + return delegate.pathOf(RepositoryUtils.toArtifact(artifact)); + } + + @Override + public String getPathForLocalMetadata(Metadata metadata) { + return delegate.pathOfLocalRepositoryMetadata(new ArtifactMetadataAdapter(metadata), delegate); + } + + @Override + public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) { + return delegate.pathOfLocalRepositoryMetadata( + new ArtifactMetadataAdapter(metadata), new ArtifactRepositoryAdapter(repository)); + } + + @Override + public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) { + String path = getPathForLocalArtifact(request.getArtifact()); + File file = new File(getRepository().getBasedir(), path); + + LocalArtifactResult result = new LocalArtifactResult(request); + if (file.isFile()) { + result.setFile(file); + result.setAvailable(true); + } + + return result; + } + + @Override + public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) { + Metadata metadata = request.getMetadata(); + + String path; + if (request.getRepository() == null) { + path = getPathForLocalMetadata(metadata); + } else { + path = getPathForRemoteMetadata(metadata, request.getRepository(), request.getContext()); + } + + File file = new File(getRepository().getBasedir(), path); + + LocalMetadataResult result = new LocalMetadataResult(request); + if (file.isFile()) { + result.setFile(file); + } + + return result; + } + + @Override + public void add(RepositorySystemSession session, LocalArtifactRegistration request) { + // noop + } + + @Override + public void add(RepositorySystemSession session, LocalMetadataRegistration request) { + // noop + } + + static class ArtifactMetadataAdapter implements ArtifactMetadata { + + private final Metadata metadata; + + ArtifactMetadataAdapter(Metadata metadata) { + this.metadata = metadata; + } + + @Override + public boolean storedInArtifactVersionDirectory() { + return !metadata.getVersion().isEmpty(); + } + + @Override + public boolean storedInGroupDirectory() { + return metadata.getArtifactId().isEmpty(); + } + + @Override + public String getGroupId() { + return nullify(metadata.getGroupId()); + } + + @Override + public String getArtifactId() { + return nullify(metadata.getArtifactId()); + } + + @Override + public String getBaseVersion() { + return nullify(metadata.getVersion()); + } + + private String nullify(String str) { + return (str == null || str.isEmpty()) ? null : str; + } + + @Override + public Object getKey() { + return metadata.toString(); + } + + @Override + public String getRemoteFilename() { + return metadata.getType(); + } + + @Override + public String getLocalFilename(ArtifactRepository repository) { + return insertRepositoryKey(getRemoteFilename(), repository.getKey()); + } + + private String insertRepositoryKey(String filename, String repositoryKey) { + String result; + int idx = filename.indexOf('.'); + if (idx < 0) { + result = filename + '-' + repositoryKey; + } else { + result = filename.substring(0, idx) + '-' + repositoryKey + filename.substring(idx); + } + return result; + } + + @Override + public void merge(org.apache.maven.repository.legacy.metadata.ArtifactMetadata metadata) { + // not used + } + + @Override + public void merge(ArtifactMetadata metadata) { + // not used + } + + @Override + public void storeInLocalRepository(ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataStoreException { + // not used + } + + @Override + public String extendedToString() { + return metadata.toString(); + } + } + + static class ArtifactRepositoryAdapter implements ArtifactRepository { + + private final RemoteRepository repository; + + ArtifactRepositoryAdapter(RemoteRepository repository) { + this.repository = repository; + } + + @Override + public String pathOf(org.apache.maven.artifact.Artifact artifact) { + return null; + } + + @Override + public String pathOfRemoteRepositoryMetadata(ArtifactMetadata artifactMetadata) { + return null; + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return null; + } + + @Override + public String getUrl() { + return repository.getUrl(); + } + + @Override + public void setUrl(String url) {} + + @Override + public String getBasedir() { + return null; + } + + @Override + public String getProtocol() { + return repository.getProtocol(); + } + + @Override + public String getId() { + return repository.getId(); + } + + @Override + public void setId(String id) {} + + @Override + public ArtifactRepositoryPolicy getSnapshots() { + return null; + } + + @Override + public void setSnapshotUpdatePolicy(ArtifactRepositoryPolicy policy) {} + + @Override + public ArtifactRepositoryPolicy getReleases() { + return null; + } + + @Override + public void setReleaseUpdatePolicy(ArtifactRepositoryPolicy policy) {} + + @Override + public ArtifactRepositoryLayout getLayout() { + return null; + } + + @Override + public void setLayout(ArtifactRepositoryLayout layout) {} + + @Override + public String getKey() { + return getId(); + } + + @Override + public boolean isUniqueVersion() { + return true; + } + + @Override + public boolean isBlacklisted() { + return false; + } + + @Override + public void setBlacklisted(boolean blackListed) {} + + @Override + public org.apache.maven.artifact.Artifact find(org.apache.maven.artifact.Artifact artifact) { + return null; + } + + @Override + public List findVersions(org.apache.maven.artifact.Artifact artifact) { + return Collections.emptyList(); + } + + @Override + public boolean isProjectAware() { + return false; + } + + @Override + public void setAuthentication(Authentication authentication) {} + + @Override + public Authentication getAuthentication() { + return null; + } + + @Override + public void setProxy(Proxy proxy) {} + + @Override + public Proxy getProxy() { + return null; + } + + @Override + public List getMirroredRepositories() { + return Collections.emptyList(); + } + + @Override + public void setMirroredRepositories(List mirroredRepositories) {} + + @Override + public boolean isBlocked() { + return false; + } + + @Override + public void setBlocked(boolean blocked) {} + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/layout/FlatRepositoryLayout.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/layout/FlatRepositoryLayout.java new file mode 100644 index 000000000000..465dd45cac2d --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/layout/FlatRepositoryLayout.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.layout; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * FlatRepositoryLayout + */ +@Named("flat") +@Singleton +@Deprecated +public class FlatRepositoryLayout implements ArtifactRepositoryLayout { + + private static final char ARTIFACT_SEPARATOR = '-'; + + private static final char GROUP_SEPARATOR = '.'; + + @Override + public String getId() { + return "flat"; + } + + @Override + public String pathOf(Artifact artifact) { + ArtifactHandler artifactHandler = artifact.getArtifactHandler(); + + StringBuilder path = new StringBuilder(128); + + path.append(artifact.getArtifactId()).append(ARTIFACT_SEPARATOR).append(artifact.getVersion()); + + if (artifact.hasClassifier()) { + path.append(ARTIFACT_SEPARATOR).append(artifact.getClassifier()); + } + + if (artifactHandler.getExtension() != null + && !artifactHandler.getExtension().isEmpty()) { + path.append(GROUP_SEPARATOR).append(artifactHandler.getExtension()); + } + + return path.toString(); + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return pathOfRepositoryMetadata(metadata.getLocalFilename(repository)); + } + + private String pathOfRepositoryMetadata(String filename) { + StringBuilder path = new StringBuilder(128); + + path.append(filename); + + return path.toString(); + } + + @Override + public String pathOfRemoteRepositoryMetadata(ArtifactMetadata metadata) { + return pathOfRepositoryMetadata(metadata.getRemoteFilename()); + } + + @Override + public String toString() { + return getId(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java new file mode 100644 index 000000000000..c9c7d83c563e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import javax.xml.stream.XMLStreamException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.metadata.v4.MetadataStaxReader; +import org.apache.maven.metadata.v4.MetadataStaxWriter; + +/** + * Shared methods of the repository metadata handling. + * + */ +@Deprecated +public abstract class AbstractRepositoryMetadata implements RepositoryMetadata { + private static final String LS = System.lineSeparator(); + + private Metadata metadata; + + protected AbstractRepositoryMetadata(Metadata metadata) { + this.metadata = metadata; + } + + @Override + public String getRemoteFilename() { + return "maven-metadata.xml"; + } + + @Override + public String getLocalFilename(ArtifactRepository repository) { + return "maven-metadata-" + repository.getKey() + ".xml"; + } + + @Override + public void storeInLocalRepository(ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataStoreException { + try { + updateRepositoryMetadata(localRepository, remoteRepository); + } catch (IOException | XMLStreamException e) { + throw new RepositoryMetadataStoreException("Error updating group repository metadata", e); + } + } + + protected void updateRepositoryMetadata(ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws IOException, XMLStreamException { + Metadata metadata = null; + + File metadataFile = new File( + localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata(this, remoteRepository)); + + if (metadataFile.length() == 0) { + if (!metadataFile.delete()) { + // sleep for 10ms just in case this is windows holding a file lock + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // ignore + } + metadataFile.delete(); // if this fails, forget about it, we'll try to overwrite it anyway so no need + // to delete on exit + } + } else if (metadataFile.exists()) { + try (InputStream input = Files.newInputStream(metadataFile.toPath())) { + metadata = new Metadata(new MetadataStaxReader().read(input, false)); + } + } + + boolean changed; + + // If file could not be found or was not valid, start from scratch + if (metadata == null) { + metadata = this.metadata; + + changed = true; + } else { + changed = metadata.merge(this.metadata); + } + + // beware meta-versions! + String version = metadata.getVersion(); + if (Artifact.LATEST_VERSION.equals(version) || Artifact.RELEASE_VERSION.equals(version)) { + // meta-versions are not valid values...don't write them. + metadata.setVersion(null); + } + + if (changed || !metadataFile.exists()) { + metadataFile.getParentFile().mkdirs(); + try (OutputStream output = Files.newOutputStream(metadataFile.toPath())) { + MetadataStaxWriter mappingWriter = new MetadataStaxWriter(); + mappingWriter.write(output, metadata.getDelegate()); + } + } else { + metadataFile.setLastModified(System.currentTimeMillis()); + } + } + + @Override + public String toString() { + return "repository metadata for: '" + getKey() + "'"; + } + + protected static Metadata createMetadata(Artifact artifact, Versioning versioning) { + Metadata metadata = new Metadata(); + metadata.setGroupId(artifact.getGroupId()); + metadata.setArtifactId(artifact.getArtifactId()); + metadata.setVersion(artifact.getVersion()); + metadata.setVersioning(versioning); + return metadata; + } + + protected static Versioning createVersioning(Snapshot snapshot) { + Versioning versioning = new Versioning(); + versioning.setSnapshot(snapshot); + return versioning; + } + + @Override + public void setMetadata(Metadata metadata) { + this.metadata = metadata; + } + + @Override + public Metadata getMetadata() { + return metadata; + } + + @Override + public void merge(org.apache.maven.repository.legacy.metadata.ArtifactMetadata metadata) { + // TODO not sure that it should assume this, maybe the calls to addMetadata should pre-merge, then artifact + // replaces? + AbstractRepositoryMetadata repoMetadata = (AbstractRepositoryMetadata) metadata; + this.metadata.merge(repoMetadata.getMetadata()); + } + + @Override + public void merge(ArtifactMetadata metadata) { + // TODO not sure that it should assume this, maybe the calls to addMetadata should pre-merge, then artifact + // replaces? + AbstractRepositoryMetadata repoMetadata = (AbstractRepositoryMetadata) metadata; + this.metadata.merge(repoMetadata.getMetadata()); + } + + @Override + public String extendedToString() { + StringBuilder buffer = new StringBuilder(256); + + buffer.append(LS).append("Repository Metadata").append(LS).append("--------------------------"); + buffer.append(LS).append("GroupId: ").append(getGroupId()); + buffer.append(LS).append("ArtifactId: ").append(getArtifactId()); + buffer.append(LS).append("Metadata Type: ").append(getClass().getName()); + + return buffer.toString(); + } + + @Override + public int getNature() { + return RELEASE; + } + + @Override + public ArtifactRepositoryPolicy getPolicy(ArtifactRepository repository) { + int nature = getNature(); + if ((nature & RepositoryMetadata.RELEASE_OR_SNAPSHOT) == RepositoryMetadata.RELEASE_OR_SNAPSHOT) { + ArtifactRepositoryPolicy policy = new ArtifactRepositoryPolicy(repository.getReleases()); + policy.merge(repository.getSnapshots()); + return policy; + } else if ((nature & RepositoryMetadata.SNAPSHOT) != 0) { + return repository.getSnapshots(); + } else { + return repository.getReleases(); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/ArtifactRepositoryMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/ArtifactRepositoryMetadata.java new file mode 100644 index 000000000000..036c201e711e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/ArtifactRepositoryMetadata.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * Metadata for the artifact directory of the repository. + * + */ +@Deprecated +public class ArtifactRepositoryMetadata extends AbstractRepositoryMetadata { + private Artifact artifact; + + public ArtifactRepositoryMetadata(Artifact artifact) { + this(artifact, null); + } + + public ArtifactRepositoryMetadata(Artifact artifact, Versioning versioning) { + super(createMetadata(artifact, versioning)); + this.artifact = artifact; + } + + @Override + public boolean storedInGroupDirectory() { + return false; + } + + @Override + public boolean storedInArtifactVersionDirectory() { + return false; + } + + @Override + public String getGroupId() { + return artifact.getGroupId(); + } + + @Override + public String getArtifactId() { + return artifact.getArtifactId(); + } + + @Override + public String getBaseVersion() { + // Don't want the artifact's version in here, as this is stored in the directory above that + return null; + } + + @Override + public Object getKey() { + return "artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId(); + } + + @Override + public boolean isSnapshot() { + // Don't consider the artifact's version in here, as this is stored in the directory above that + return false; + } + + @Override + public int getNature() { + if (artifact.getVersion() != null) { + return artifact.isSnapshot() ? SNAPSHOT : RELEASE; + } + + VersionRange range = artifact.getVersionRange(); + if (range != null) { + for (Restriction restriction : range.getRestrictions()) { + if (isSnapshot(restriction.getLowerBound()) || isSnapshot(restriction.getUpperBound())) { + return RELEASE_OR_SNAPSHOT; + } + } + } + + return RELEASE; + } + + private boolean isSnapshot(ArtifactVersion version) { + return version != null && ArtifactUtils.isSnapshot(version.getQualifier()); + } + + @Override + public ArtifactRepository getRepository() { + return null; + } + + @Override + public void setRepository(ArtifactRepository remoteRepository) { + /* + * NOTE: Metadata at the g:a level contains a collection of available versions. After merging, we can't tell + * which repository provides which version so the metadata manager must not restrict the artifact resolution to + * the repository with the most recent updates. + */ + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java new file mode 100644 index 000000000000..a7d052455ad7 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java @@ -0,0 +1,436 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.xml.stream.XMLStreamException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.DefaultRepositoryRequest; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.metadata.v4.MetadataStaxReader; +import org.apache.maven.metadata.v4.MetadataStaxWriter; +import org.apache.maven.repository.legacy.UpdateCheckManager; +import org.apache.maven.repository.legacy.WagonManager; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.codehaus.plexus.logging.AbstractLogEnabled; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultRepositoryMetadataManager extends AbstractLogEnabled implements RepositoryMetadataManager { + @Inject + private WagonManager wagonManager; + + @Inject + private UpdateCheckManager updateCheckManager; + + @Override + public void resolve( + RepositoryMetadata metadata, + List remoteRepositories, + ArtifactRepository localRepository) + throws RepositoryMetadataResolutionException { + RepositoryRequest request = new DefaultRepositoryRequest(); + request.setLocalRepository(localRepository); + request.setRemoteRepositories(remoteRepositories); + resolve(metadata, request); + } + + @Override + public void resolve(RepositoryMetadata metadata, RepositoryRequest request) + throws RepositoryMetadataResolutionException { + ArtifactRepository localRepo = request.getLocalRepository(); + List remoteRepositories = request.getRemoteRepositories(); + + if (!request.isOffline()) { + Date localCopyLastModified = null; + if (metadata.getBaseVersion() != null) { + localCopyLastModified = getLocalCopyLastModified(localRepo, metadata); + } + + for (ArtifactRepository repository : remoteRepositories) { + ArtifactRepositoryPolicy policy = metadata.getPolicy(repository); + + File file = + new File(localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata(metadata, repository)); + boolean update; + + if (!policy.isEnabled()) { + update = false; + + if (getLogger().isDebugEnabled()) { + getLogger() + .debug("Skipping update check for " + metadata.getKey() + " (" + file + + ") from disabled repository " + repository.getId() + " (" + + repository.getUrl() + ")"); + } + } else if (request.isForceUpdate()) { + update = true; + } else if (localCopyLastModified != null && !policy.checkOutOfDate(localCopyLastModified)) { + update = false; + + if (getLogger().isDebugEnabled()) { + getLogger() + .debug("Skipping update check for " + metadata.getKey() + " (" + file + + ") from repository " + repository.getId() + " (" + repository.getUrl() + + ") in favor of local copy"); + } + } else { + update = updateCheckManager.isUpdateRequired(metadata, repository, file); + } + + if (update) { + getLogger().info(metadata.getKey() + ": checking for updates from " + repository.getId()); + try { + wagonManager.getArtifactMetadata(metadata, repository, file, policy.getChecksumPolicy()); + } catch (ResourceDoesNotExistException e) { + getLogger().debug(metadata + " could not be found on repository: " + repository.getId()); + + // delete the local copy so the old details aren't used. + if (file.exists()) { + if (!file.delete()) { + // sleep for 10ms just in case this is windows holding a file lock + try { + Thread.sleep(10); + } catch (InterruptedException ie) { + // ignore + } + file.delete(); // if this fails, forget about it + } + } + } catch (TransferFailedException e) { + getLogger() + .warn(metadata + " could not be retrieved from repository: " + repository.getId() + + " due to an error: " + e.getMessage()); + getLogger().debug("Exception", e); + } finally { + updateCheckManager.touch(metadata, repository, file); + } + } + + // TODO should this be inside the above check? + // touch file so that this is not checked again until interval has passed + if (file.exists()) { + file.setLastModified(System.currentTimeMillis()); + } + } + } + + try { + mergeMetadata(metadata, remoteRepositories, localRepo); + } catch (RepositoryMetadataStoreException e) { + throw new RepositoryMetadataResolutionException( + "Unable to store local copy of metadata: " + e.getMessage(), e); + } + } + + private Date getLocalCopyLastModified(ArtifactRepository localRepository, RepositoryMetadata metadata) { + String metadataPath = localRepository.pathOfLocalRepositoryMetadata(metadata, localRepository); + File metadataFile = new File(localRepository.getBasedir(), metadataPath); + return metadataFile.isFile() ? new Date(metadataFile.lastModified()) : null; + } + + private void mergeMetadata( + RepositoryMetadata metadata, + List remoteRepositories, + ArtifactRepository localRepository) + throws RepositoryMetadataStoreException { + // TODO currently this is first wins, but really we should take the latest by comparing either the + // snapshot timestamp, or some other timestamp later encoded into the metadata. + // TODO this needs to be repeated here so the merging doesn't interfere with the written metadata + // - we'd be much better having a pristine input, and an ongoing metadata for merging instead + + Map previousMetadata = new HashMap<>(); + ArtifactRepository selected = null; + for (ArtifactRepository repository : remoteRepositories) { + ArtifactRepositoryPolicy policy = metadata.getPolicy(repository); + + if (policy.isEnabled() && loadMetadata(metadata, repository, localRepository, previousMetadata)) { + metadata.setRepository(repository); + selected = repository; + } + } + if (loadMetadata(metadata, localRepository, localRepository, previousMetadata)) { + metadata.setRepository(null); + selected = localRepository; + } + + updateSnapshotMetadata(metadata, previousMetadata, selected, localRepository); + } + + private void updateSnapshotMetadata( + RepositoryMetadata metadata, + Map previousMetadata, + ArtifactRepository selected, + ArtifactRepository localRepository) + throws RepositoryMetadataStoreException { + // TODO this could be a lot nicer... should really be in the snapshot transformation? + if (metadata.isSnapshot()) { + Metadata prevMetadata = metadata.getMetadata(); + + for (ArtifactRepository repository : previousMetadata.keySet()) { + Metadata m = previousMetadata.get(repository); + if (repository.equals(selected)) { + if (m.getVersioning() == null) { + m.setVersioning(new Versioning()); + } + + if (m.getVersioning().getSnapshot() == null) { + m.getVersioning().setSnapshot(new Snapshot()); + } + } else { + if ((m.getVersioning() != null) + && (m.getVersioning().getSnapshot() != null) + && m.getVersioning().getSnapshot().isLocalCopy()) { + m.getVersioning().getSnapshot().setLocalCopy(false); + metadata.setMetadata(m); + metadata.storeInLocalRepository(localRepository, repository); + } + } + } + + metadata.setMetadata(prevMetadata); + } + } + + private boolean loadMetadata( + RepositoryMetadata repoMetadata, + ArtifactRepository remoteRepository, + ArtifactRepository localRepository, + Map previousMetadata) { + boolean setRepository = false; + + File metadataFile = new File( + localRepository.getBasedir(), + localRepository.pathOfLocalRepositoryMetadata(repoMetadata, remoteRepository)); + + if (metadataFile.exists()) { + Metadata metadata; + + try { + metadata = readMetadata(metadataFile); + } catch (RepositoryMetadataReadException e) { + if (getLogger().isDebugEnabled()) { + getLogger().warn(e.getMessage(), e); + } else { + getLogger().warn(e.getMessage()); + } + return setRepository; + } + + if (repoMetadata.isSnapshot() && (previousMetadata != null)) { + previousMetadata.put(remoteRepository, metadata); + } + + if (repoMetadata.getMetadata() != null) { + setRepository = repoMetadata.getMetadata().merge(metadata); + } else { + repoMetadata.setMetadata(metadata); + setRepository = true; + } + } + return setRepository; + } + + /* + * TODO share with DefaultPluginMappingManager. + */ + protected Metadata readMetadata(File mappingFile) throws RepositoryMetadataReadException { + + try (InputStream in = Files.newInputStream(mappingFile.toPath())) { + return new Metadata(new MetadataStaxReader().read(in, false)); + } catch (FileNotFoundException e) { + throw new RepositoryMetadataReadException("Cannot read metadata from '" + mappingFile + "'", e); + } catch (IOException | XMLStreamException e) { + throw new RepositoryMetadataReadException( + "Cannot read metadata from '" + mappingFile + "': " + e.getMessage(), e); + } + } + + /** + * Ensures the last updated timestamp of the specified metadata does not refer to the future and fixes the local + * metadata if necessary to allow proper merging/updating of metadata during deployment. + */ + private void fixTimestamp(File metadataFile, Metadata metadata, Metadata reference) { + boolean changed = false; + + if (metadata != null && reference != null) { + Versioning versioning = metadata.getVersioning(); + Versioning versioningRef = reference.getVersioning(); + if (versioning != null && versioningRef != null) { + String lastUpdated = versioning.getLastUpdated(); + String now = versioningRef.getLastUpdated(); + if (lastUpdated != null && now != null && now.compareTo(lastUpdated) < 0) { + getLogger() + .warn("The last updated timestamp in " + metadataFile + " refers to the future (now = " + + now + + ", lastUpdated = " + lastUpdated + "). Please verify that the clocks of all" + + " deploying machines are reasonably synchronized."); + versioning.setLastUpdated(now); + changed = true; + } + } + } + + if (changed) { + getLogger().debug("Repairing metadata in " + metadataFile); + + try (OutputStream out = Files.newOutputStream(metadataFile.toPath())) { + new MetadataStaxWriter().write(out, metadata.getDelegate()); + } catch (IOException | XMLStreamException e) { + String msg = "Could not write fixed metadata to " + metadataFile + ": " + e.getMessage(); + if (getLogger().isDebugEnabled()) { + getLogger().warn(msg, e); + } else { + getLogger().warn(msg); + } + } + } + } + + @Override + public void resolveAlways( + RepositoryMetadata metadata, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataResolutionException { + File file; + try { + file = getArtifactMetadataFromDeploymentRepository(metadata, localRepository, remoteRepository); + } catch (TransferFailedException e) { + throw new RepositoryMetadataResolutionException( + metadata + " could not be retrieved from repository: " + remoteRepository.getId() + + " due to an error: " + e.getMessage(), + e); + } + + try { + if (file.exists()) { + Metadata prevMetadata = readMetadata(file); + metadata.setMetadata(prevMetadata); + } + } catch (RepositoryMetadataReadException e) { + throw new RepositoryMetadataResolutionException(e.getMessage(), e); + } + } + + private File getArtifactMetadataFromDeploymentRepository( + ArtifactMetadata metadata, ArtifactRepository localRepo, ArtifactRepository remoteRepository) + throws TransferFailedException { + File file = + new File(localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata(metadata, remoteRepository)); + + try { + wagonManager.getArtifactMetadataFromDeploymentRepository( + metadata, remoteRepository, file, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); + } catch (ResourceDoesNotExistException e) { + getLogger() + .info(metadata + " could not be found on repository: " + remoteRepository.getId() + + ", so will be created"); + + // delete the local copy so the old details aren't used. + if (file.exists()) { + if (!file.delete()) { + // sleep for 10ms just in case this is windows holding a file lock + try { + Thread.sleep(10); + } catch (InterruptedException ie) { + // ignore + } + file.delete(); // if this fails, forget about it + } + } + } finally { + if (metadata instanceof RepositoryMetadata repositoryMetadata) { + updateCheckManager.touch(repositoryMetadata, remoteRepository, file); + } + } + return file; + } + + @Override + public void deploy( + ArtifactMetadata metadata, ArtifactRepository localRepository, ArtifactRepository deploymentRepository) + throws RepositoryMetadataDeploymentException { + File file; + if (metadata instanceof RepositoryMetadata repositoryMetadata) { + getLogger().info("Retrieving previous metadata from " + deploymentRepository.getId()); + try { + file = getArtifactMetadataFromDeploymentRepository(metadata, localRepository, deploymentRepository); + } catch (TransferFailedException e) { + throw new RepositoryMetadataDeploymentException( + metadata + " could not be retrieved from repository: " + deploymentRepository.getId() + + " due to an error: " + e.getMessage(), + e); + } + + if (file.isFile()) { + try { + fixTimestamp(file, readMetadata(file), repositoryMetadata.getMetadata()); + } catch (RepositoryMetadataReadException e) { + // will be reported via storeInlocalRepository + } + } + } else { + // It's a POM - we don't need to retrieve it first + file = new File( + localRepository.getBasedir(), + localRepository.pathOfLocalRepositoryMetadata(metadata, deploymentRepository)); + } + + try { + metadata.storeInLocalRepository(localRepository, deploymentRepository); + } catch (RepositoryMetadataStoreException e) { + throw new RepositoryMetadataDeploymentException("Error installing metadata: " + e.getMessage(), e); + } + + try { + wagonManager.putArtifactMetadata(file, metadata, deploymentRepository); + } catch (TransferFailedException e) { + throw new RepositoryMetadataDeploymentException("Error while deploying metadata: " + e.getMessage(), e); + } + } + + @Override + public void install(ArtifactMetadata metadata, ArtifactRepository localRepository) + throws RepositoryMetadataInstallationException { + try { + metadata.storeInLocalRepository(localRepository, localRepository); + } catch (RepositoryMetadataStoreException e) { + throw new RepositoryMetadataInstallationException("Error installing metadata: " + e.getMessage(), e); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/GroupRepositoryMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/GroupRepositoryMetadata.java new file mode 100644 index 000000000000..884649dc240c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/GroupRepositoryMetadata.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * Metadata for the group directory of the repository. + * + */ +@Deprecated +public class GroupRepositoryMetadata extends AbstractRepositoryMetadata { + private final String groupId; + + public GroupRepositoryMetadata(String groupId) { + super(new Metadata()); + this.groupId = groupId; + } + + @Override + public boolean storedInGroupDirectory() { + return true; + } + + @Override + public boolean storedInArtifactVersionDirectory() { + return false; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public String getArtifactId() { + return null; + } + + @Override + public String getBaseVersion() { + return null; + } + + public void addPluginMapping(String goalPrefix, String artifactId) { + addPluginMapping(goalPrefix, artifactId, artifactId); + } + + public void addPluginMapping(String goalPrefix, String artifactId, String name) { + List plugins = getMetadata().getPlugins(); + boolean found = false; + for (Iterator i = plugins.iterator(); i.hasNext() && !found; ) { + Plugin plugin = i.next(); + if (plugin.getPrefix().equals(goalPrefix)) { + found = true; + } + } + if (!found) { + Plugin plugin = new Plugin(); + plugin.setPrefix(goalPrefix); + plugin.setArtifactId(artifactId); + plugin.setName(name); + + getMetadata().addPlugin(plugin); + } + } + + @Override + public Object getKey() { + return groupId; + } + + @Override + public boolean isSnapshot() { + return false; + } + + @Override + public ArtifactRepository getRepository() { + return null; + } + + @Override + public void setRepository(ArtifactRepository remoteRepository) { + // intentionally blank + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataBridge.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataBridge.java new file mode 100644 index 000000000000..acdccd6e1469 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataBridge.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; + +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultArtifactRepository; +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.metadata.AbstractMetadata; +import org.eclipse.aether.metadata.MergeableMetadata; +import org.eclipse.aether.metadata.Metadata; + +/** + * Warning: This is an internal utility class that is only public for technical reasons, it is not part + * of the public API. In particular, this class can be changed or deleted without prior notice. + * + */ +@Deprecated +public final class MetadataBridge extends AbstractMetadata implements MergeableMetadata { + + private ArtifactMetadata metadata; + + private boolean merged; + + public MetadataBridge(ArtifactMetadata metadata) { + this.metadata = metadata; + } + + @Override + public void merge(File current, File result) throws RepositoryException { + try { + if (current.exists()) { + Files.createDirectories(result.toPath().getParent()); + Files.copy(current.toPath(), result.toPath()); + } + ArtifactRepository localRepo = new MetadataRepository(result); + metadata.storeInLocalRepository(localRepo, localRepo); + merged = true; + } catch (Exception e) { + throw new RepositoryException(e.getMessage(), e); + } + } + + @Override + public boolean isMerged() { + return merged; + } + + @Override + public String getGroupId() { + return emptify(metadata.getGroupId()); + } + + @Override + public String getArtifactId() { + return metadata.storedInGroupDirectory() ? "" : emptify(metadata.getArtifactId()); + } + + @Override + public String getVersion() { + return metadata.storedInArtifactVersionDirectory() ? emptify(metadata.getBaseVersion()) : ""; + } + + @Override + public String getType() { + return metadata.getRemoteFilename(); + } + + private String emptify(String string) { + return (string != null) ? string : ""; + } + + @Override + public File getFile() { + return null; + } + + @Override + public MetadataBridge setFile(File file) { + return this; + } + + @Override + public Path getPath() { + return null; + } + + @Override + public Nature getNature() { + if (metadata instanceof RepositoryMetadata repositoryMetadata) { + return switch (repositoryMetadata.getNature()) { + case RepositoryMetadata.RELEASE_OR_SNAPSHOT -> Nature.RELEASE_OR_SNAPSHOT; + case RepositoryMetadata.SNAPSHOT -> Nature.SNAPSHOT; + default -> Nature.RELEASE; + }; + } else { + return Nature.RELEASE; + } + } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } + + @Override + public Metadata setProperties(Map properties) { + return this; + } + + @SuppressWarnings("deprecation") + static class MetadataRepository extends DefaultArtifactRepository { + + private File metadataFile; + + MetadataRepository(File metadataFile) { + super("local", "", null); + this.metadataFile = metadataFile; + } + + @Override + public String getBasedir() { + return metadataFile.getParent(); + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return metadataFile.getName(); + } + } +} diff --git a/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataUtils.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataUtils.java similarity index 86% rename from maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataUtils.java rename to compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataUtils.java index bdc4a795ad15..1ef6090012cc 100644 --- a/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataUtils.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/MetadataUtils.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.repository.metadata; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,22 +16,19 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.repository.metadata; /** * Assists in handling repository metadata. * - * @author Benjamin Bentmann */ -class MetadataUtils -{ +@Deprecated +class MetadataUtils { - public static Metadata cloneMetadata( Metadata src ) - { - if ( src == null ) - { + public static Metadata cloneMetadata(Metadata src) { + if (src == null) { return null; } return src.clone(); } - } diff --git a/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadata.java similarity index 85% rename from maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadata.java rename to compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadata.java index d7fe50b4303c..078ce1175a78 100644 --- a/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadata.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadata.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.repository.metadata; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.repository.metadata; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; @@ -25,12 +24,10 @@ /** * Describes repository directory metadata. * - * @author Brett Porter * TODO not happy about the store method - they use "this" */ -public interface RepositoryMetadata - extends org.apache.maven.artifact.metadata.ArtifactMetadata -{ +@Deprecated +public interface RepositoryMetadata extends org.apache.maven.artifact.metadata.ArtifactMetadata { int RELEASE = 1; @@ -50,7 +47,7 @@ public interface RepositoryMetadata * * @param remoteRepository the repository */ - void setRepository( ArtifactRepository remoteRepository ); + void setRepository(ArtifactRepository remoteRepository); /** * Get the repository metadata associated with this marker. @@ -64,7 +61,7 @@ public interface RepositoryMetadata * * @param metadata the metadata */ - void setMetadata( Metadata metadata ); + void setMetadata(Metadata metadata); /** * Whether this represents a snapshot. @@ -87,6 +84,5 @@ public interface RepositoryMetadata * @param repository The repository for which to determine the policy, must not be {@code null}. * @return The policy, never {@code null}. */ - ArtifactRepositoryPolicy getPolicy( ArtifactRepository repository ); - + ArtifactRepositoryPolicy getPolicy(ArtifactRepository repository); } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataDeploymentException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataDeploymentException.java new file mode 100644 index 000000000000..8e374e9a6411 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataDeploymentException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +/** + * Error while deploying repository metadata. + * + */ +@Deprecated +public class RepositoryMetadataDeploymentException extends Throwable { + public RepositoryMetadataDeploymentException(String message) { + super(message); + } + + public RepositoryMetadataDeploymentException(String message, Exception e) { + super(message, e); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataInstallationException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataInstallationException.java new file mode 100644 index 000000000000..314b89ba1caf --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataInstallationException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +/** + * Error while installing repository metadata. + * + */ +@Deprecated +public class RepositoryMetadataInstallationException extends Throwable { + public RepositoryMetadataInstallationException(String message) { + super(message); + } + + public RepositoryMetadataInstallationException(String message, Exception e) { + super(message, e); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataManager.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataManager.java new file mode 100644 index 000000000000..b92eddcd561a --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataManager.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import java.util.List; + +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; + +/** + * RepositoryMetadataManager + */ +@Deprecated +public interface RepositoryMetadataManager { + + void resolve(RepositoryMetadata repositoryMetadata, RepositoryRequest repositoryRequest) + throws RepositoryMetadataResolutionException; + + void resolve( + RepositoryMetadata repositoryMetadata, + List repositories, + ArtifactRepository localRepository) + throws RepositoryMetadataResolutionException; + + void resolveAlways( + RepositoryMetadata metadata, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataResolutionException; + + /** + * Deploy metadata to the remote repository. + * + * @param metadata the metadata to deploy + * @param localRepository the local repository to install to first + * @param deploymentRepository the remote repository to deploy to + * @throws RepositoryMetadataDeploymentException in case of metadata deployment issue + */ + void deploy(ArtifactMetadata metadata, ArtifactRepository localRepository, ArtifactRepository deploymentRepository) + throws RepositoryMetadataDeploymentException; + + /** + * Install the metadata in the local repository. + * + * @param metadata the metadata + * @param localRepository the local repository + * @throws RepositoryMetadataInstallationException in case of metadata installation issue + */ + void install(ArtifactMetadata metadata, ArtifactRepository localRepository) + throws RepositoryMetadataInstallationException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataReadException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataReadException.java new file mode 100644 index 000000000000..8b588fb5d5e5 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataReadException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +/** + * Problem storing the repository metadata in the local repository. + * + */ +@Deprecated +public class RepositoryMetadataReadException extends Exception { + public RepositoryMetadataReadException(String message) { + super(message); + } + + public RepositoryMetadataReadException(String message, Exception e) { + super(message, e); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataResolutionException.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataResolutionException.java new file mode 100644 index 000000000000..50782b33170e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/RepositoryMetadataResolutionException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +/** + * Error while retrieving repository metadata from the repository. + * + */ +@Deprecated +public class RepositoryMetadataResolutionException extends Exception { + public RepositoryMetadataResolutionException(String message) { + super(message); + } + + public RepositoryMetadataResolutionException(String message, Exception e) { + super(message, e); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/SnapshotArtifactRepositoryMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/SnapshotArtifactRepositoryMetadata.java new file mode 100644 index 000000000000..45914e8ba010 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/SnapshotArtifactRepositoryMetadata.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository.metadata; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * Metadata for the artifact version directory of the repository. + * + * TODO split instantiation (versioning, plugin mappings) from definition + */ +@Deprecated +public class SnapshotArtifactRepositoryMetadata extends AbstractRepositoryMetadata { + private Artifact artifact; + + public SnapshotArtifactRepositoryMetadata(Artifact artifact) { + super(createMetadata(artifact, null)); + this.artifact = artifact; + } + + public SnapshotArtifactRepositoryMetadata(Artifact artifact, Snapshot snapshot) { + super(createMetadata(artifact, createVersioning(snapshot))); + this.artifact = artifact; + } + + @Override + public boolean storedInGroupDirectory() { + return false; + } + + @Override + public boolean storedInArtifactVersionDirectory() { + return true; + } + + @Override + public String getGroupId() { + return artifact.getGroupId(); + } + + @Override + public String getArtifactId() { + return artifact.getArtifactId(); + } + + @Override + public String getBaseVersion() { + return artifact.getBaseVersion(); + } + + @Override + public Object getKey() { + return "snapshot " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getBaseVersion(); + } + + @Override + public boolean isSnapshot() { + return artifact.isSnapshot(); + } + + @Override + public int getNature() { + return isSnapshot() ? SNAPSHOT : RELEASE; + } + + @Override + public ArtifactRepository getRepository() { + return artifact.getRepository(); + } + + @Override + public void setRepository(ArtifactRepository remoteRepository) { + artifact.setRepository(remoteRepository); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactCollector.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactCollector.java new file mode 100644 index 000000000000..bdddced4c301 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactCollector.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; + +/** + * Artifact collector - takes a set of original artifacts and resolves the best versions to use + * along with their metadata. No artifacts are downloaded. + */ +@Deprecated +public interface ArtifactCollector extends org.apache.maven.repository.legacy.resolver.LegacyArtifactCollector { + + @Deprecated + ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners) + throws ArtifactResolutionException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionRequest.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionRequest.java new file mode 100644 index 000000000000..ec86b40a06d7 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionRequest.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryCache; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Proxy; +import org.apache.maven.settings.Server; + +/** + * A resolution request allows you to either use an existing MavenProject, or a coordinate (gid:aid:version) + * to process a POMs dependencies. + * + */ +@Deprecated +public class ArtifactResolutionRequest implements RepositoryRequest { + private static final String LS = System.lineSeparator(); + + private Artifact artifact; + + // Needs to go away + // These are really overrides now, projects defining dependencies for a plugin that override what is + // specified in the plugin itself. + private Set artifactDependencies; + + private ArtifactRepository localRepository; + + private List remoteRepositories; + + private ArtifactFilter collectionFilter; + + private ArtifactFilter resolutionFilter; + + // Needs to go away + private List listeners = new ArrayList<>(); + + // This is like a filter but overrides all transitive versions + private Map managedVersionMap; + + private boolean resolveRoot = true; + + private boolean resolveTransitively = false; + + private boolean offline; + + private boolean forceUpdate; + + private List servers; + + private List mirrors; + + private List proxies; + + public ArtifactResolutionRequest() { + // nothing here + } + + public ArtifactResolutionRequest(RepositoryRequest request) { + setLocalRepository(request.getLocalRepository()); + setRemoteRepositories(request.getRemoteRepositories()); + setOffline(request.isOffline()); + setForceUpdate(request.isForceUpdate()); + } + + public Artifact getArtifact() { + return artifact; + } + + public ArtifactResolutionRequest setArtifact(Artifact artifact) { + this.artifact = artifact; + + return this; + } + + public ArtifactResolutionRequest setArtifactDependencies(Set artifactDependencies) { + this.artifactDependencies = artifactDependencies; + + return this; + } + + public Set getArtifactDependencies() { + return artifactDependencies; + } + + @Override + public ArtifactRepository getLocalRepository() { + return localRepository; + } + + @Override + public ArtifactResolutionRequest setLocalRepository(ArtifactRepository localRepository) { + this.localRepository = localRepository; + + return this; + } + + @Override + public List getRemoteRepositories() { + return remoteRepositories; + } + + @Override + public ArtifactResolutionRequest setRemoteRepositories(List remoteRepositories) { + this.remoteRepositories = remoteRepositories; + + return this; + } + + /** + * Gets the artifact filter that controls traversal of the dependency graph. + * + * @return The filter used to determine which of the artifacts in the dependency graph should be traversed or + * {@code null} to collect all transitive dependencies. + */ + public ArtifactFilter getCollectionFilter() { + return collectionFilter; + } + + public ArtifactResolutionRequest setCollectionFilter(ArtifactFilter filter) { + this.collectionFilter = filter; + + return this; + } + + /** + * Gets the artifact filter that controls downloading of artifact files. This filter operates on those artifacts + * that have been included by the {@link #getCollectionFilter()}. + * + * @return The filter used to determine which of the artifacts should have their files resolved or {@code null} to + * resolve the files for all collected artifacts. + */ + public ArtifactFilter getResolutionFilter() { + return resolutionFilter; + } + + public ArtifactResolutionRequest setResolutionFilter(ArtifactFilter filter) { + this.resolutionFilter = filter; + + return this; + } + + public List getListeners() { + return listeners; + } + + public ArtifactResolutionRequest setListeners(List listeners) { + this.listeners = listeners; + + return this; + } + + public ArtifactResolutionRequest addListener(ResolutionListener listener) { + listeners.add(listener); + + return this; + } + + public Map getManagedVersionMap() { + return managedVersionMap; + } + + public ArtifactResolutionRequest setManagedVersionMap(Map managedVersionMap) { + this.managedVersionMap = managedVersionMap; + + return this; + } + + public ArtifactResolutionRequest setResolveRoot(boolean resolveRoot) { + this.resolveRoot = resolveRoot; + + return this; + } + + public boolean isResolveRoot() { + return resolveRoot; + } + + public ArtifactResolutionRequest setResolveTransitively(boolean resolveDependencies) { + this.resolveTransitively = resolveDependencies; + + return this; + } + + public boolean isResolveTransitively() { + return resolveTransitively; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder() + .append("REQUEST: ") + .append(LS) + .append("artifact: ") + .append(artifact) + .append(LS) + .append(artifactDependencies) + .append(LS) + .append("localRepository: ") + .append(localRepository) + .append(LS) + .append("remoteRepositories: ") + .append(remoteRepositories); + + return sb.toString(); + } + + @Override + public boolean isOffline() { + return offline; + } + + @Override + public ArtifactResolutionRequest setOffline(boolean offline) { + this.offline = offline; + + return this; + } + + @Override + public boolean isForceUpdate() { + return forceUpdate; + } + + @Override + public ArtifactResolutionRequest setForceUpdate(boolean forceUpdate) { + this.forceUpdate = forceUpdate; + + return this; + } + + public ArtifactResolutionRequest setServers(List servers) { + this.servers = servers; + + return this; + } + + public List getServers() { + if (servers == null) { + servers = new ArrayList<>(); + } + + return servers; + } + + public ArtifactResolutionRequest setMirrors(List mirrors) { + this.mirrors = mirrors; + + return this; + } + + public List getMirrors() { + if (mirrors == null) { + mirrors = new ArrayList<>(); + } + + return mirrors; + } + + public ArtifactResolutionRequest setProxies(List proxies) { + this.proxies = proxies; + + return this; + } + + public List getProxies() { + if (proxies == null) { + proxies = new ArrayList<>(); + } + + return proxies; + } + + // + // Used by Tycho and will break users and force them to upgrade to Maven 3.1 so we should really leave + // this here, possibly indefinitely. + // + public ArtifactResolutionRequest setCache(RepositoryCache cache) { + return this; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionResult.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionResult.java new file mode 100644 index 000000000000..be3c565fc0d1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolutionResult.java @@ -0,0 +1,328 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; + +/** + * Specific problems during resolution that we want to account for: + *
      + *
    • missing metadata
    • + *
    • version range violations
    • + *
    • version circular dependencies
    • + *
    • missing artifacts
    • + *
    • network/transfer errors
    • + *
    • file system errors: permissions
    • + *
    + * + * TODO carlos: all these possible has*Exceptions and get*Exceptions methods make the clients too + * complex requiring a long list of checks, need to create a parent/interface/encapsulation + * for the types of exceptions + */ +@Deprecated +public class ArtifactResolutionResult { + private static final String LS = System.lineSeparator(); + + private Artifact originatingArtifact; + + private List missingArtifacts; + + // Exceptions + + private List exceptions; + + private List versionRangeViolations; + + private List metadataResolutionExceptions; + + private List circularDependencyExceptions; + + private List errorArtifactExceptions; + + // file system errors + + private List repositories; + + private Set artifacts; + + private Set resolutionNodes; + + public Artifact getOriginatingArtifact() { + return originatingArtifact; + } + + public ArtifactResolutionResult setOriginatingArtifact(final Artifact originatingArtifact) { + this.originatingArtifact = originatingArtifact; + + return this; + } + + public void addArtifact(Artifact artifact) { + if (artifacts == null) { + artifacts = new LinkedHashSet<>(); + } + + artifacts.add(artifact); + } + + public Set getArtifacts() { + if (artifacts == null) { + artifacts = new LinkedHashSet<>(); + } + + return artifacts; + } + + public void setArtifacts(Set artifacts) { + this.artifacts = artifacts; + } + + public Set getArtifactResolutionNodes() { + if (resolutionNodes == null) { + resolutionNodes = new LinkedHashSet<>(); + } + + return resolutionNodes; + } + + public void setArtifactResolutionNodes(Set resolutionNodes) { + this.resolutionNodes = resolutionNodes; + } + + public boolean hasMissingArtifacts() { + return missingArtifacts != null && !missingArtifacts.isEmpty(); + } + + public List getMissingArtifacts() { + return missingArtifacts == null ? Collections.emptyList() : Collections.unmodifiableList(missingArtifacts); + } + + public ArtifactResolutionResult addMissingArtifact(Artifact artifact) { + missingArtifacts = initList(missingArtifacts); + + missingArtifacts.add(artifact); + + return this; + } + + public ArtifactResolutionResult setUnresolvedArtifacts(final List unresolvedArtifacts) { + this.missingArtifacts = unresolvedArtifacts; + + return this; + } + + public boolean isSuccess() { + return !(hasMissingArtifacts() || hasExceptions()); + } + + // ------------------------------------------------------------------------ + // Exceptions + // ------------------------------------------------------------------------ + + public boolean hasExceptions() { + return exceptions != null && !exceptions.isEmpty(); + } + + public List getExceptions() { + return exceptions == null ? Collections.emptyList() : Collections.unmodifiableList(exceptions); + } + + // ------------------------------------------------------------------------ + // Version Range Violations + // ------------------------------------------------------------------------ + + public boolean hasVersionRangeViolations() { + return versionRangeViolations != null; + } + + /** + * TODO this needs to accept a {@link OverConstrainedVersionException} as returned by + * {@link #getVersionRangeViolation(int)} but it's not used like that in + * DefaultLegacyArtifactCollector + * + * @param e an exception + * @return {@code this} + */ + public ArtifactResolutionResult addVersionRangeViolation(Exception e) { + versionRangeViolations = initList(versionRangeViolations); + + versionRangeViolations.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public OverConstrainedVersionException getVersionRangeViolation(int i) { + return (OverConstrainedVersionException) versionRangeViolations.get(i); + } + + public List getVersionRangeViolations() { + return versionRangeViolations == null + ? Collections.emptyList() + : Collections.unmodifiableList(versionRangeViolations); + } + + // ------------------------------------------------------------------------ + // Metadata Resolution Exceptions: ArtifactResolutionExceptions + // ------------------------------------------------------------------------ + + public boolean hasMetadataResolutionExceptions() { + return metadataResolutionExceptions != null; + } + + public ArtifactResolutionResult addMetadataResolutionException(ArtifactResolutionException e) { + metadataResolutionExceptions = initList(metadataResolutionExceptions); + + metadataResolutionExceptions.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public ArtifactResolutionException getMetadataResolutionException(int i) { + return metadataResolutionExceptions.get(i); + } + + public List getMetadataResolutionExceptions() { + return metadataResolutionExceptions == null + ? Collections.emptyList() + : Collections.unmodifiableList(metadataResolutionExceptions); + } + + // ------------------------------------------------------------------------ + // ErrorArtifactExceptions: ArtifactResolutionExceptions + // ------------------------------------------------------------------------ + + public boolean hasErrorArtifactExceptions() { + return errorArtifactExceptions != null; + } + + public ArtifactResolutionResult addErrorArtifactException(ArtifactResolutionException e) { + errorArtifactExceptions = initList(errorArtifactExceptions); + + errorArtifactExceptions.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public List getErrorArtifactExceptions() { + if (errorArtifactExceptions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(errorArtifactExceptions); + } + + // ------------------------------------------------------------------------ + // Circular Dependency Exceptions + // ------------------------------------------------------------------------ + + public boolean hasCircularDependencyExceptions() { + return circularDependencyExceptions != null; + } + + public ArtifactResolutionResult addCircularDependencyException(CyclicDependencyException e) { + circularDependencyExceptions = initList(circularDependencyExceptions); + + circularDependencyExceptions.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public CyclicDependencyException getCircularDependencyException(int i) { + return circularDependencyExceptions.get(i); + } + + public List getCircularDependencyExceptions() { + if (circularDependencyExceptions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(circularDependencyExceptions); + } + + // ------------------------------------------------------------------------ + // Repositories + // ------------------------------------------------------------------------ + + public List getRepositories() { + if (repositories == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(repositories); + } + + public ArtifactResolutionResult setRepositories(final List repositories) { + this.repositories = repositories; + + return this; + } + + // + // Internal + // + + private List initList(final List l) { + if (l == null) { + return new ArrayList<>(); + } + return l; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + if (artifacts != null) { + int i = 1; + sb.append("---------").append(LS); + sb.append(artifacts.size()).append(LS); + for (Artifact a : artifacts) { + sb.append(i).append(' ').append(a).append(LS); + i++; + } + sb.append("---------"); + } + + return sb.toString(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolver.java new file mode 100644 index 000000000000..ccdd808d123c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ArtifactResolver.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.wagon.events.TransferListener; + +/** + */ +// Just hide the one method we want behind the RepositorySystem interface. +@Deprecated +public interface ArtifactResolver { + + ArtifactResolutionResult resolve(ArtifactResolutionRequest request); + + // The rest is deprecated + // USED BY MAVEN ASSEMBLY PLUGIN 2.2-beta-2 + @Deprecated + String ROLE = ArtifactResolver.class.getName(); + + // USED BY SUREFIRE, DEPENDENCY PLUGIN + @Deprecated + ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter) + throws ArtifactResolutionException, ArtifactNotFoundException; + + // USED BY MAVEN ASSEMBLY PLUGIN + @Deprecated + ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source) + throws ArtifactResolutionException, ArtifactNotFoundException; + + // USED BY MAVEN ASSEMBLY PLUGIN + @Deprecated + ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter) + throws ArtifactResolutionException, ArtifactNotFoundException; + + // USED BY INVOKER PLUGIN + @Deprecated + ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + List remoteRepositories, + ArtifactRepository localRepository, + ArtifactMetadataSource source) + throws ArtifactResolutionException, ArtifactNotFoundException; + + @Deprecated + @SuppressWarnings("checkstyle:parameternumber") + ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners) + throws ArtifactResolutionException, ArtifactNotFoundException; + + @Deprecated + ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + List remoteRepositories, + ArtifactRepository localRepository, + ArtifactMetadataSource source, + List listeners) + throws ArtifactResolutionException, ArtifactNotFoundException; + + // USED BY REMOTE RESOURCES PLUGIN, DEPENDENCY PLUGIN, SHADE PLUGIN + @Deprecated + void resolve(Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException; + + // USED BY REMOTE RESOURCES PLUGIN + @Deprecated + void resolve( + Artifact artifact, + List remoteRepositories, + ArtifactRepository localRepository, + TransferListener downloadMonitor) + throws ArtifactResolutionException, ArtifactNotFoundException; + + // USED BY DEPENDENCY PLUGIN, ARCHETYPE DOWNLOADER + @Deprecated + void resolveAlways( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DebugResolutionListener.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DebugResolutionListener.java new file mode 100644 index 000000000000..708ee91b14d4 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DebugResolutionListener.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.VersionRange; +import org.codehaus.plexus.logging.Logger; + +/** + * Send resolution events to the debug log. + * + */ +@Deprecated +public class DebugResolutionListener implements ResolutionListener, ResolutionListenerForDepMgmt { + private Logger logger; + + private String indent = ""; + + private static Set ignoredArtifacts = new HashSet<>(); + + public DebugResolutionListener(Logger logger) { + this.logger = logger; + } + + @Override + public void testArtifact(Artifact node) {} + + @Override + public void startProcessChildren(Artifact artifact) { + indent += " "; + } + + @Override + public void endProcessChildren(Artifact artifact) { + indent = indent.substring(2); + } + + @Override + public void includeArtifact(Artifact artifact) { + logger.debug(indent + artifact + " (selected for " + artifact.getScope() + ")"); + } + + @Override + public void omitForNearer(Artifact omitted, Artifact kept) { + String omittedVersion = omitted.getVersion(); + String keptVersion = kept.getVersion(); + + if (!Objects.equals(omittedVersion, keptVersion)) { + logger.debug(indent + omitted + " (removed - nearer found: " + keptVersion + ")"); + } + } + + @Override + public void omitForCycle(Artifact omitted) { + logger.debug(indent + omitted + " (removed - causes a cycle in the graph)"); + } + + @Override + public void updateScopeCurrentPom(Artifact artifact, String ignoredScope) { + logger.debug(indent + artifact + " (not setting artifactScope to: " + ignoredScope + "; local artifactScope " + + artifact.getScope() + " wins)"); + + // TODO better way than static? this might hide messages in a reactor + if (!ignoredArtifacts.contains(artifact)) { + logger.warn("\n\tArtifact " + artifact + " retains local artifactScope '" + artifact.getScope() + + "' overriding broader artifactScope '" + ignoredScope + "'\n" + + "\tgiven by a dependency. If this is not intended, modify or remove the local artifactScope.\n"); + ignoredArtifacts.add(artifact); + } + } + + @Override + public void updateScope(Artifact artifact, String scope) { + logger.debug(indent + artifact + " (setting artifactScope to: " + scope + ")"); + } + + @Override + public void selectVersionFromRange(Artifact artifact) { + logger.debug(indent + artifact + " (setting version to: " + artifact.getVersion() + " from range: " + + artifact.getVersionRange() + ")"); + } + + @Override + public void restrictRange(Artifact artifact, Artifact replacement, VersionRange newRange) { + logger.debug(indent + artifact + " (range restricted from: " + artifact.getVersionRange() + " and: " + + replacement.getVersionRange() + " to: " + newRange + " )"); + } + + /** + * The logic used here used to be a copy of the logic used in the DefaultArtifactCollector, and this method was + * called right before the actual version/artifactScope changes were done. However, a different set of conditionals + * (and more information) is needed to be able to determine when and if the version and/or artifactScope changes. + * See the two added methods, manageArtifactVersion and manageArtifactScope. + */ + @Override + public void manageArtifact(Artifact artifact, Artifact replacement) { + String msg = indent + artifact; + msg += " ("; + if (replacement.getVersion() != null) { + msg += "applying version: " + replacement.getVersion() + ";"; + } + if (replacement.getScope() != null) { + msg += "applying artifactScope: " + replacement.getScope(); + } + msg += ")"; + logger.debug(msg); + } + + @Override + public void manageArtifactVersion(Artifact artifact, Artifact replacement) { + // only show msg if a change is actually taking place + if (!replacement.getVersion().equals(artifact.getVersion())) { + String msg = indent + artifact + " (applying version: " + replacement.getVersion() + ")"; + logger.debug(msg); + } + } + + @Override + public void manageArtifactScope(Artifact artifact, Artifact replacement) { + // only show msg if a change is actually taking place + if (!replacement.getScope().equals(artifact.getScope())) { + String msg = indent + artifact + " (applying artifactScope: " + replacement.getScope() + ")"; + logger.debug(msg); + } + } + + @Override + public void manageArtifactSystemPath(Artifact artifact, Artifact replacement) { + // only show msg if a change is actually taking place + if (!replacement.getScope().equals(artifact.getScope())) { + String msg = indent + artifact + " (applying system path: " + replacement.getFile() + ")"; + logger.debug(msg); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultArtifactCollector.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultArtifactCollector.java new file mode 100644 index 000000000000..6001f46e7076 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultArtifactCollector.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import javax.inject.Named; +import javax.inject.Singleton; + +/** + * Artifact collector - takes a set of original artifacts and resolves the best versions to use + * along with their metadata. No artifacts are downloaded. + */ +@Deprecated +@Named +@Singleton +public class DefaultArtifactCollector extends org.apache.maven.repository.legacy.resolver.DefaultLegacyArtifactCollector + implements ArtifactCollector {} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultArtifactResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultArtifactResolver.java new file mode 100644 index 000000000000..68ad69a6d2cd --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultArtifactResolver.java @@ -0,0 +1,624 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.repository.metadata.Snapshot; +import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver; +import org.apache.maven.wagon.events.TransferListener; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.LocalRepositoryManager; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultArtifactResolver implements ArtifactResolver, Disposable { + @Inject + private Logger logger; + + @Inject + protected ArtifactFactory artifactFactory; + + @Inject + private ArtifactCollector artifactCollector; + + @Inject + private ResolutionErrorHandler resolutionErrorHandler; + + @Inject + private ArtifactMetadataSource source; + + @Inject + private PlexusContainer container; + + @Inject + private LegacySupport legacySupport; + + private final Executor executor; + + public DefaultArtifactResolver() { + int threads = Integer.getInteger("maven.artifact.threads", 5); + if (threads <= 1) { + executor = Runnable::run; + } else { + executor = new ThreadPoolExecutor( + threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new DaemonThreadCreator()); + } + } + + private RepositorySystemSession getSession(ArtifactRepository localRepository) { + return LegacyLocalRepositoryManager.overlay(localRepository, legacySupport.getRepositorySession(), null); + } + + private void injectSession1(RepositoryRequest request, MavenSession session) { + if (session != null) { + request.setOffline(session.isOffline()); + request.setForceUpdate(session.getRequest().isUpdateSnapshots()); + } + } + + private void injectSession2(ArtifactResolutionRequest request, MavenSession session) { + injectSession1(request, session); + + if (session != null) { + request.setServers(session.getRequest().getServers()); + request.setMirrors(session.getRequest().getMirrors()); + request.setProxies(session.getRequest().getProxies()); + } + } + + @Override + public void resolve( + Artifact artifact, + List remoteRepositories, + ArtifactRepository localRepository, + TransferListener resolutionListener) + throws ArtifactResolutionException, ArtifactNotFoundException { + resolve(artifact, remoteRepositories, getSession(localRepository)); + } + + @Override + public void resolveAlways( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException { + resolve(artifact, remoteRepositories, getSession(localRepository)); + } + + private void resolve( + Artifact artifact, List remoteRepositories, RepositorySystemSession session) + throws ArtifactResolutionException, ArtifactNotFoundException { + if (artifact == null) { + return; + } + + if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) { + File systemFile = artifact.getFile(); + + if (systemFile == null) { + throw new ArtifactNotFoundException("System artifact: " + artifact + " has no file attached", artifact); + } + + if (!systemFile.exists()) { + throw new ArtifactNotFoundException( + "System artifact: " + artifact + " not found in path: " + systemFile, artifact); + } + + if (!systemFile.isFile()) { + throw new ArtifactNotFoundException( + "System artifact: " + artifact + " is not a file: " + systemFile, artifact); + } + + artifact.setResolved(true); + + return; + } + + if (!artifact.isResolved()) { + ArtifactResult result; + + try { + ArtifactRequest artifactRequest = new ArtifactRequest(); + artifactRequest.setArtifact(RepositoryUtils.toArtifact(artifact)); + artifactRequest.setRepositories(RepositoryUtils.toRepos(remoteRepositories)); + + // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not + LocalRepositoryManager lrm = session.getLocalRepositoryManager(); + String path = lrm.getPathForLocalArtifact(artifactRequest.getArtifact()); + artifact.setFile(new File(lrm.getRepository().getBasedir(), path)); + + RepositorySystem repoSystem = container.lookup(RepositorySystem.class); + result = repoSystem.resolveArtifact(session, artifactRequest); + } catch (ComponentLookupException e) { + throw new IllegalStateException("Unable to lookup " + RepositorySystem.class.getName()); + } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) { + if (e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException) { + throw new ArtifactNotFoundException(e.getMessage(), artifact, remoteRepositories, e); + } else { + throw new ArtifactResolutionException(e.getMessage(), artifact, remoteRepositories, e); + } + } + + artifact.selectVersion(result.getArtifact().getVersion()); + artifact.setFile(result.getArtifact().getFile()); + artifact.setResolved(true); + + if (artifact.isSnapshot()) { + Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher(artifact.getVersion()); + if (matcher.matches()) { + Snapshot snapshot = new Snapshot(); + snapshot.setTimestamp(matcher.group(2)); + try { + snapshot.setBuildNumber(Integer.parseInt(matcher.group(3))); + artifact.addMetadata(new SnapshotArtifactRepositoryMetadata(artifact, snapshot)); + } catch (NumberFormatException e) { + logger.warn("Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage()); + } + } + } + } + } + + @Override + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveTransitively( + artifacts, + originatingArtifact, + Collections.emptyMap(), + localRepository, + remoteRepositories, + source, + filter); + } + + @Override + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveTransitively( + artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source, null); + } + + @Override + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveTransitively( + artifacts, + originatingArtifact, + managedVersions, + localRepository, + remoteRepositories, + source, + filter, + null); + } + + @Override + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + List remoteRepositories, + ArtifactRepository localRepository, + ArtifactMetadataSource source) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveTransitively(artifacts, originatingArtifact, localRepository, remoteRepositories, source, null); + } + + @Override + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + List remoteRepositories, + ArtifactRepository localRepository, + ArtifactMetadataSource source, + List listeners) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveTransitively( + artifacts, + originatingArtifact, + Collections.emptyMap(), + localRepository, + remoteRepositories, + source, + null, + listeners); + } + + @Override + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners) + throws ArtifactResolutionException, ArtifactNotFoundException { + return resolveTransitively( + artifacts, + originatingArtifact, + managedVersions, + localRepository, + remoteRepositories, + source, + filter, + listeners, + null); + } + + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactResolutionResult resolveTransitively( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners, + List conflictResolvers) + throws ArtifactResolutionException, ArtifactNotFoundException { + ArtifactResolutionRequest request = new ArtifactResolutionRequest() + .setArtifact(originatingArtifact) + .setResolveRoot(false) + . + // This is required by the surefire plugin + setArtifactDependencies(artifacts) + .setManagedVersionMap(managedVersions) + .setLocalRepository(localRepository) + .setRemoteRepositories(remoteRepositories) + .setCollectionFilter(filter) + .setListeners(listeners); + + injectSession2(request, legacySupport.getSession()); + + return resolveWithExceptions(request); + } + + public ArtifactResolutionResult resolveWithExceptions(ArtifactResolutionRequest request) + throws ArtifactResolutionException, ArtifactNotFoundException { + ArtifactResolutionResult result = resolve(request); + + // We have collected all the problems so let's mimic the way the old code worked and just blow up right here. + // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting + // users. Bad dog! + + resolutionErrorHandler.throwErrors(request, result); + + return result; + } + + // ------------------------------------------------------------------------ + // + // ------------------------------------------------------------------------ + + @Override + @SuppressWarnings("checkstyle:methodlength") + public ArtifactResolutionResult resolve(ArtifactResolutionRequest request) { + Artifact rootArtifact = request.getArtifact(); + Set artifacts = request.getArtifactDependencies(); + Map managedVersions = request.getManagedVersionMap(); + List listeners = request.getListeners(); + ArtifactFilter collectionFilter = request.getCollectionFilter(); + ArtifactFilter resolutionFilter = request.getResolutionFilter(); + RepositorySystemSession session = getSession(request.getLocalRepository()); + + // TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the + // workspace + if (source == null) { + try { + source = container.lookup(ArtifactMetadataSource.class); + } catch (ComponentLookupException e) { + // won't happen + } + } + + if (listeners == null) { + listeners = new ArrayList<>(); + + if (logger.isDebugEnabled()) { + listeners.add(new DebugResolutionListener(logger)); + } + + listeners.add(new WarningResolutionListener(logger)); + } + + ArtifactResolutionResult result = new ArtifactResolutionResult(); + + // The root artifact may, or may not be resolved so we need to check before we attempt to resolve. + // This is often an artifact like a POM that is taken from disk and we already have hold of the + // file reference. But this may be a Maven Plugin that we need to resolve from a remote repository + // as well as its dependencies. + + if (request.isResolveRoot() /* && rootArtifact.getFile() == null */) { + try { + resolve(rootArtifact, request.getRemoteRepositories(), session); + } catch (ArtifactResolutionException e) { + result.addErrorArtifactException(e); + return result; + } catch (ArtifactNotFoundException e) { + result.addMissingArtifact(request.getArtifact()); + return result; + } + } + + ArtifactResolutionRequest collectionRequest = request; + + if (request.isResolveTransitively()) { + MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest(request); + + metadataRequest.setArtifact(rootArtifact); + metadataRequest.setResolveManagedVersions(managedVersions == null); + + try { + ResolutionGroup resolutionGroup = source.retrieve(metadataRequest); + + if (managedVersions == null) { + managedVersions = resolutionGroup.getManagedVersions(); + } + + Set directArtifacts = resolutionGroup.getArtifacts(); + + if (artifacts == null || artifacts.isEmpty()) { + artifacts = directArtifacts; + } else { + List allArtifacts = new ArrayList<>(); + allArtifacts.addAll(artifacts); + allArtifacts.addAll(directArtifacts); + + Map mergedArtifacts = new LinkedHashMap<>(); + for (Artifact artifact : allArtifacts) { + String conflictId = artifact.getDependencyConflictId(); + if (!mergedArtifacts.containsKey(conflictId)) { + mergedArtifacts.put(conflictId, artifact); + } + } + + artifacts = new LinkedHashSet<>(mergedArtifacts.values()); + } + + collectionRequest = new ArtifactResolutionRequest(request); + collectionRequest.setServers(request.getServers()); + collectionRequest.setMirrors(request.getMirrors()); + collectionRequest.setProxies(request.getProxies()); + collectionRequest.setRemoteRepositories(resolutionGroup.getResolutionRepositories()); + } catch (ArtifactMetadataRetrievalException e) { + ArtifactResolutionException are = new ArtifactResolutionException( + "Unable to get dependency information for " + rootArtifact.getId() + ": " + e.getMessage(), + rootArtifact, + metadataRequest.getRemoteRepositories(), + e); + result.addMetadataResolutionException(are); + return result; + } + } + + if (artifacts == null || artifacts.isEmpty()) { + if (request.isResolveRoot()) { + result.addArtifact(rootArtifact); + } + return result; + } + + // After the collection we will have the artifact object in the result but they will not be resolved yet. + result = artifactCollector.collect( + artifacts, rootArtifact, managedVersions, collectionRequest, source, collectionFilter, listeners, null); + + // We have metadata retrieval problems, or there are cycles that have been detected + // so we give this back to the calling code and let them deal with this information + // appropriately. + + if (result.hasMetadataResolutionExceptions() + || result.hasVersionRangeViolations() + || result.hasCircularDependencyExceptions()) { + return result; + } + + if (result.getArtifactResolutionNodes() != null) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + CountDownLatch latch = + new CountDownLatch(result.getArtifactResolutionNodes().size()); + + for (ResolutionNode node : result.getArtifactResolutionNodes()) { + Artifact artifact = node.getArtifact(); + + if (resolutionFilter == null || resolutionFilter.include(artifact)) { + executor.execute(new ResolveTask( + classLoader, latch, artifact, session, node.getRemoteRepositories(), result)); + } else { + latch.countDown(); + } + } + try { + latch.await(); + } catch (InterruptedException e) { + result.addErrorArtifactException( + new ArtifactResolutionException("Resolution interrupted", rootArtifact, e)); + } + } + + // We want to send the root artifact back in the result but we need to do this after the other dependencies + // have been resolved. + if (request.isResolveRoot()) { + // Add the root artifact (as the first artifact to retain logical order of class path!) + Set allArtifacts = new LinkedHashSet<>(); + allArtifacts.add(rootArtifact); + allArtifacts.addAll(result.getArtifacts()); + result.setArtifacts(allArtifacts); + } + + return result; + } + + @Override + public void resolve( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException { + resolve(artifact, remoteRepositories, localRepository, null); + } + + /** + * ThreadCreator for creating daemon threads with fixed ThreadGroup-name. + */ + @Deprecated + static final class DaemonThreadCreator implements ThreadFactory { + static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver"; + + static final ThreadGroup GROUP = new ThreadGroup(THREADGROUP_NAME); + + static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1); + + @Override + public Thread newThread(Runnable r) { + Thread newThread = new Thread(GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement()); + newThread.setDaemon(true); + newThread.setContextClassLoader(null); + return newThread; + } + } + + private class ResolveTask implements Runnable { + + private final ClassLoader classLoader; + + private final CountDownLatch latch; + + private final Artifact artifact; + + private final RepositorySystemSession session; + + private final List remoteRepositories; + + private final ArtifactResolutionResult result; + + ResolveTask( + ClassLoader classLoader, + CountDownLatch latch, + Artifact artifact, + RepositorySystemSession session, + List remoteRepositories, + ArtifactResolutionResult result) { + this.classLoader = classLoader; + this.latch = latch; + this.artifact = artifact; + this.session = session; + this.remoteRepositories = remoteRepositories; + this.result = result; + } + + @Override + public void run() { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(classLoader); + resolve(artifact, remoteRepositories, session); + } catch (ArtifactNotFoundException anfe) { + // These are cases where the artifact just isn't present in any of the remote repositories + // because it wasn't deployed, or it was deployed in the wrong place. + + synchronized (result) { + result.addMissingArtifact(artifact); + } + } catch (ArtifactResolutionException e) { + // This is really a wagon TransferFailedException so something went wrong after we successfully + // retrieved the metadata. + + synchronized (result) { + result.addErrorArtifactException(e); + } + } finally { + latch.countDown(); + Thread.currentThread().setContextClassLoader(old); + } + } + } + + @Override + public void dispose() { + if (executor instanceof ExecutorService executorService) { + executorService.shutdownNow(); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultResolutionErrorHandler.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultResolutionErrorHandler.java new file mode 100644 index 000000000000..768edbcec516 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/DefaultResolutionErrorHandler.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultResolutionErrorHandler implements ResolutionErrorHandler { + + @Override + public void throwErrors(ArtifactResolutionRequest request, ArtifactResolutionResult result) + throws ArtifactResolutionException { + // Metadata cannot be found + + if (result.hasMetadataResolutionExceptions()) { + throw result.getMetadataResolutionException(0); + } + + // Metadata cannot be retrieved + + // Cyclic Dependency Error + + if (result.hasCircularDependencyExceptions()) { + throw result.getCircularDependencyException(0); + } + + // Version Range Violation + + if (result.hasVersionRangeViolations()) { + throw result.getVersionRangeViolation(0); + } + + // Transfer Error + + if (result.hasErrorArtifactExceptions()) { + throw result.getErrorArtifactExceptions().get(0); + } + + if (result.hasMissingArtifacts()) { + throw new MultipleArtifactsNotFoundException( + request.getArtifact(), + toList(result.getArtifacts()), + result.getMissingArtifacts(), + request.getRemoteRepositories()); + } + + // this should never happen since we checked all possible error sources before but better be sure + if (result.hasExceptions()) { + throw new ArtifactResolutionException( + "Unknown error during artifact resolution, " + request + ", " + result.getExceptions(), + request.getArtifact(), + request.getRemoteRepositories()); + } + } + + private static List toList(Collection items) { + return (items != null) ? new ArrayList<>(items) : null; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/artifact/resolver/ResolutionErrorHandler.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionErrorHandler.java similarity index 80% rename from maven-core/src/main/java/org/apache/maven/artifact/resolver/ResolutionErrorHandler.java rename to compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionErrorHandler.java index c54e5b6c7027..ccd8688ec566 100644 --- a/maven-core/src/main/java/org/apache/maven/artifact/resolver/ResolutionErrorHandler.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionErrorHandler.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.resolver; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,14 +16,13 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.resolver; /** - * @author Benjamin Bentmann */ -public interface ResolutionErrorHandler -{ - - void throwErrors( ArtifactResolutionRequest request, ArtifactResolutionResult result ) - throws ArtifactResolutionException; +@Deprecated +public interface ResolutionErrorHandler { + void throwErrors(ArtifactResolutionRequest request, ArtifactResolutionResult result) + throws ArtifactResolutionException; } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionListener.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionListener.java new file mode 100644 index 000000000000..f292039f8f1f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionListener.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * Listens to the resolution process and handles events. + */ +@Deprecated +public interface ResolutionListener { + String ROLE = ResolutionListener.class.getName(); + + int TEST_ARTIFACT = 1; + + int PROCESS_CHILDREN = 2; + + int FINISH_PROCESSING_CHILDREN = 3; + + int INCLUDE_ARTIFACT = 4; + + int OMIT_FOR_NEARER = 5; + + int UPDATE_SCOPE = 6; + + @Deprecated + int MANAGE_ARTIFACT = 7; + + int OMIT_FOR_CYCLE = 8; + + /** + * this event means that the artifactScope has NOT been updated to a farther node artifactScope because current + * node is in the first level pom + */ + int UPDATE_SCOPE_CURRENT_POM = 9; + + int SELECT_VERSION_FROM_RANGE = 10; + + int RESTRICT_RANGE = 11; + + int MANAGE_ARTIFACT_VERSION = 12; + + int MANAGE_ARTIFACT_SCOPE = 13; + + int MANAGE_ARTIFACT_SYSTEM_PATH = 14; + + void testArtifact(Artifact node); + + void startProcessChildren(Artifact artifact); + + void endProcessChildren(Artifact artifact); + + void includeArtifact(Artifact artifact); + + void omitForNearer(Artifact omitted, Artifact kept); + + void updateScope(Artifact artifact, String scope); + + @Deprecated + void manageArtifact(Artifact artifact, Artifact replacement); + + // TODO Remove ResolutionListenerDM interface + + void omitForCycle(Artifact artifact); + + /** + * This event means that the artifactScope has NOT been updated to a farther node artifactScope because current + * node is in the first level pom + * + * @param artifact current node artifact, the one in the first level pom + * @param ignoredScope artifactScope that was ignored because artifact was in first level pom + */ + void updateScopeCurrentPom(Artifact artifact, String ignoredScope); + + void selectVersionFromRange(Artifact artifact); + + void restrictRange(Artifact artifact, Artifact replacement, VersionRange newRange); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionListenerForDepMgmt.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionListenerForDepMgmt.java new file mode 100644 index 000000000000..288af42c2cf4 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionListenerForDepMgmt.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import org.apache.maven.artifact.Artifact; + +/** + * Do not use! + *

    + * Should only be implemented by DebugResolutionListener. Remove this + * when the ResolutionListener interface deprecation of the manageArtifact + * method (and the [yet to be done] addition of these methods to that + * interface) has had a chance to propagate to all interested plugins. + */ +@Deprecated +public interface ResolutionListenerForDepMgmt { + void manageArtifactVersion(Artifact artifact, Artifact replacement); + + void manageArtifactScope(Artifact artifact, Artifact replacement); + + void manageArtifactSystemPath(Artifact artifact, Artifact replacement); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionNode.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionNode.java new file mode 100644 index 000000000000..16e621719726 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/ResolutionNode.java @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; + +/** + * ResolutionNode + */ +@Deprecated +public class ResolutionNode { + private Artifact artifact; + + private List children; + + private final List parents; + + private final int depth; + + private final ResolutionNode parent; + + private final List remoteRepositories; + + private boolean active = true; + + private List trail; + + public ResolutionNode(Artifact artifact, List remoteRepositories) { + this.artifact = artifact; + this.remoteRepositories = remoteRepositories; + depth = 0; + parents = Collections.emptyList(); + parent = null; + } + + public ResolutionNode(Artifact artifact, List remoteRepositories, ResolutionNode parent) { + this.artifact = artifact; + this.remoteRepositories = remoteRepositories; + depth = parent.depth + 1; + parents = new ArrayList<>(); + parents.addAll(parent.parents); + parents.add(parent.getKey()); + this.parent = parent; + } + + public Artifact getArtifact() { + return artifact; + } + + public Object getKey() { + return artifact.getDependencyConflictId(); + } + + public void addDependencies( + Set artifacts, List remoteRepositories, ArtifactFilter filter) + throws CyclicDependencyException, OverConstrainedVersionException { + if (artifacts != null && !artifacts.isEmpty()) { + children = new ArrayList<>(artifacts.size()); + + for (Artifact a : artifacts) { + if (parents.contains(a.getDependencyConflictId())) { + a.setDependencyTrail(getDependencyTrail()); + + throw new CyclicDependencyException("A dependency has introduced a cycle", a); + } + + children.add(new ResolutionNode(a, remoteRepositories, this)); + } + children = Collections.unmodifiableList(children); + } else { + children = Collections.emptyList(); + } + trail = null; + } + + /** + * @return {@link List} < {@link String} > with artifact ids + * @throws OverConstrainedVersionException if version specification is over constrained + */ + public List getDependencyTrail() throws OverConstrainedVersionException { + List trial = getTrail(); + + List ret = new ArrayList<>(trial.size()); + + for (Artifact artifact : trial) { + ret.add(artifact.getId()); + } + + return ret; + } + + private List getTrail() throws OverConstrainedVersionException { + if (trail == null) { + List ids = new LinkedList<>(); + ResolutionNode node = this; + while (node != null) { + Artifact artifact = node.getArtifact(); + if (artifact.getVersion() == null) { + // set the recommended version + ArtifactVersion selected = artifact.getSelectedVersion(); + // MNG-2123: null is a valid response to getSelectedVersion, don't + // assume it won't ever be. + if (selected != null) { + artifact.selectVersion(selected.toString()); + } else { + throw new OverConstrainedVersionException( + "Unable to get a selected Version for " + artifact.getArtifactId(), artifact); + } + } + + ids.add(0, artifact); + node = node.parent; + } + trail = ids; + } + return trail; + } + + public boolean isResolved() { + return children != null; + } + + /** + * Test whether the node is direct or transitive dependency. + * + * @return whether the node is direct or transitive dependency + */ + public boolean isChildOfRootNode() { + return parent != null && parent.parent == null; + } + + public Iterator getChildrenIterator() { + return children.iterator(); + } + + public int getDepth() { + return depth; + } + + public List getRemoteRepositories() { + return remoteRepositories; + } + + public boolean isActive() { + return active; + } + + public void enable() { + active = true; + + // TODO if it was null, we really need to go find them now... or is this taken care of by the ordering? + if (children != null) { + for (ResolutionNode node : children) { + node.enable(); + } + } + } + + public void disable() { + active = false; + if (children != null) { + for (ResolutionNode node : children) { + node.disable(); + } + } + } + + public boolean filterTrail(ArtifactFilter filter) throws OverConstrainedVersionException { + boolean success = true; + if (filter != null) { + for (Artifact artifact : getTrail()) { + if (!filter.include(artifact)) { + success = false; + } + } + } + return success; + } + + @Override + public String toString() { + return artifact.toString() + " (" + depth + "; " + (active ? "enabled" : "disabled") + ")"; + } + + public void setArtifact(Artifact artifact) { + this.artifact = artifact; + } +} diff --git a/maven-compat/src/main/java/org/apache/maven/artifact/resolver/UnresolvedArtifacts.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/UnresolvedArtifacts.java similarity index 76% rename from maven-compat/src/main/java/org/apache/maven/artifact/resolver/UnresolvedArtifacts.java rename to compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/UnresolvedArtifacts.java index b9ccf7067ebf..4e6016c6eff3 100644 --- a/maven-compat/src/main/java/org/apache/maven/artifact/resolver/UnresolvedArtifacts.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/UnresolvedArtifacts.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.resolver; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.resolver; import java.util.List; @@ -28,20 +27,17 @@ * A simple recording of the Artifacts that could not be resolved for a given resolution request, along with * the remote repositories where attempts were made to resolve the artifacts. * - * @author Jason van Zyl */ -public class UnresolvedArtifacts -{ +@Deprecated +public class UnresolvedArtifacts { private Artifact originatingArtifact; private List artifacts; private List remoteRepositories; - public UnresolvedArtifacts( Artifact originatingArtifact, - List artifacts, - List remoteRepositories ) - { + public UnresolvedArtifacts( + Artifact originatingArtifact, List artifacts, List remoteRepositories) { this.originatingArtifact = originatingArtifact; this.artifacts = artifacts; @@ -49,18 +45,15 @@ public UnresolvedArtifacts( Artifact originatingArtifact, this.remoteRepositories = remoteRepositories; } - public Artifact getOriginatingArtifact() - { + public Artifact getOriginatingArtifact() { return originatingArtifact; } - public List getArtifacts() - { + public List getArtifacts() { return artifacts; } - public List getRemoteRepositories() - { + public List getRemoteRepositories() { return remoteRepositories; } } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/WarningResolutionListener.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/WarningResolutionListener.java new file mode 100644 index 000000000000..8c7fd7b670bb --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/WarningResolutionListener.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.VersionRange; +import org.codehaus.plexus.logging.Logger; + +/** + * Send resolution warning events to the warning log. + * + */ +@Deprecated +public class WarningResolutionListener implements ResolutionListener { + private Logger logger; + + public WarningResolutionListener(Logger logger) { + this.logger = logger; + } + + @Override + public void testArtifact(Artifact node) {} + + @Override + public void startProcessChildren(Artifact artifact) {} + + @Override + public void endProcessChildren(Artifact artifact) {} + + @Override + public void includeArtifact(Artifact artifact) {} + + @Override + public void omitForNearer(Artifact omitted, Artifact kept) {} + + @Override + public void omitForCycle(Artifact omitted) {} + + @Override + public void updateScopeCurrentPom(Artifact artifact, String scope) {} + + @Override + public void updateScope(Artifact artifact, String scope) {} + + @Override + public void manageArtifact(Artifact artifact, Artifact replacement) {} + + @Override + public void selectVersionFromRange(Artifact artifact) {} + + @Override + public void restrictRange(Artifact artifact, Artifact replacement, VersionRange newRange) {} +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/InversionArtifactFilter.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/InversionArtifactFilter.java new file mode 100644 index 000000000000..d8cadea36233 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/InversionArtifactFilter.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import org.apache.maven.artifact.Artifact; + +/** + * InversionArtifactFilter + */ +@Deprecated +public class InversionArtifactFilter implements ArtifactFilter { + private final ArtifactFilter toInvert; + + public InversionArtifactFilter(ArtifactFilter toInvert) { + this.toInvert = toInvert; + } + + @Override + public boolean include(Artifact artifact) { + return !toInvert.include(artifact); + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + toInvert.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof InversionArtifactFilter other) { + return toInvert.equals(other.toInvert); + } else { + return false; + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilter.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilter.java new file mode 100644 index 000000000000..0f7c2215a4c1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilter.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; + +/** + * Apply multiple filters, accepting an artifact if at least one of the filters accepts it. + * + */ +@Deprecated +public class OrArtifactFilter implements ArtifactFilter { + + private Set filters; + + public OrArtifactFilter() { + this.filters = new LinkedHashSet<>(); + } + + public OrArtifactFilter(Collection filters) { + this.filters = new LinkedHashSet<>(filters); + } + + @Override + public boolean include(Artifact artifact) { + for (ArtifactFilter filter : filters) { + if (filter.include(artifact)) { + return true; + } + } + + return false; + } + + public void add(ArtifactFilter artifactFilter) { + filters.add(artifactFilter); + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + filters.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof OrArtifactFilter other) { + return filters.equals(other.filters); + } else { + return false; + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/TypeArtifactFilter.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/TypeArtifactFilter.java new file mode 100644 index 000000000000..bea3ab6b468d --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/resolver/filter/TypeArtifactFilter.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import org.apache.maven.artifact.Artifact; + +/** Artifact Filter which filters on artifact type */ +@Deprecated +public class TypeArtifactFilter implements ArtifactFilter { + private String type = "jar"; + + public TypeArtifactFilter(String type) { + this.type = type; + } + + @Override + public boolean include(Artifact artifact) { + return type.equals(artifact.getType()); + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + type.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof TypeArtifactFilter other) { + return type.equals(other.type); + } else { + return false; + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/artifact/versioning/ManagedVersionMap.java b/compat/maven-compat/src/main/java/org/apache/maven/artifact/versioning/ManagedVersionMap.java new file mode 100644 index 000000000000..7f02150402a0 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/artifact/versioning/ManagedVersionMap.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.versioning; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.maven.artifact.Artifact; + +/** + * ManagedVersionMap + */ +@Deprecated +public class ManagedVersionMap extends HashMap { + public ManagedVersionMap(Map map) { + super(); + if (map != null) { + putAll(map); + } + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder("ManagedVersionMap (" + size() + " entries)\n"); + Iterator iter = keySet().iterator(); + while (iter.hasNext()) { + String key = iter.next(); + buffer.append(key).append('=').append(get(key)); + if (iter.hasNext()) { + buffer.append('\n'); + } + } + return buffer.toString(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/execution/DefaultRuntimeInformation.java b/compat/maven-compat/src/main/java/org/apache/maven/execution/DefaultRuntimeInformation.java new file mode 100644 index 000000000000..3a1263f541dd --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/execution/DefaultRuntimeInformation.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.execution; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; + +/** + * Describes runtime information about the application. + * + */ +@Deprecated +@Named +@Singleton +public class DefaultRuntimeInformation implements RuntimeInformation, Initializable { + + @Inject + private org.apache.maven.rtinfo.RuntimeInformation rtInfo; + + private ArtifactVersion applicationVersion; + + @Override + public ArtifactVersion getApplicationVersion() { + return applicationVersion; + } + + @Override + public void initialize() throws InitializationException { + String mavenVersion = rtInfo.getMavenVersion(); + + if (mavenVersion == null || mavenVersion.isEmpty()) { + throw new InitializationException("Unable to read Maven version from maven-core"); + } + + applicationVersion = new DefaultArtifactVersion(mavenVersion); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/execution/RuntimeInformation.java b/compat/maven-compat/src/main/java/org/apache/maven/execution/RuntimeInformation.java new file mode 100644 index 000000000000..e16946516128 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/execution/RuntimeInformation.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.execution; + +import org.apache.maven.artifact.versioning.ArtifactVersion; + +/** + * Describes runtime information about the application. + * + * @deprecated Use {@link org.apache.maven.rtinfo.RuntimeInformation} instead. + */ +@Deprecated +public interface RuntimeInformation { + ArtifactVersion getApplicationVersion(); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/internal/compat/interactivity/LegacyPlexusInteractivity.java b/compat/maven-compat/src/main/java/org/apache/maven/internal/compat/interactivity/LegacyPlexusInteractivity.java new file mode 100644 index 000000000000..f56bd1cedc82 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/internal/compat/interactivity/LegacyPlexusInteractivity.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.internal.compat.interactivity; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.api.annotations.Experimental; +import org.codehaus.plexus.components.interactivity.InputHandler; +import org.codehaus.plexus.components.interactivity.OutputHandler; +import org.codehaus.plexus.components.interactivity.Prompter; +import org.codehaus.plexus.components.interactivity.PrompterException; +import org.eclipse.sisu.Priority; + +/** + * This class is injected into any legacy component that would want to use legacy "Plexus Interactivity API". + * It simply delegates to Maven4 API {@link org.apache.maven.api.services.Prompter}. + */ +@Experimental +@Named +@Singleton +@Priority(10) +public class LegacyPlexusInteractivity implements Prompter, InputHandler, OutputHandler { + private final org.apache.maven.api.services.Prompter prompter; + + @Inject + public LegacyPlexusInteractivity(org.apache.maven.api.services.Prompter prompter) { + this.prompter = prompter; + } + + @Override + public String readLine() throws IOException { + try { + return prompter.prompt(null); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new IOException("Unable to prompt", e); + } + } + + @Override + public String readPassword() throws IOException { + try { + return prompter.promptForPassword(null); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new IOException("Unable to prompt", e); + } + } + + @Override + public List readMultipleLines() throws IOException { + List lines = new ArrayList<>(); + for (String line = readLine(); line != null && !line.isEmpty(); line = readLine()) { + lines.add(line); + } + return lines; + } + + @Override + public void write(String line) throws IOException { + try { + prompter.showMessage(line); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new IOException("Unable to prompt", e); + } + } + + @Override + public void writeLine(String line) throws IOException { + try { + prompter.showMessage(line + System.lineSeparator()); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new IOException("Unable to prompt", e); + } + } + + @Override + public String prompt(String message) throws PrompterException { + try { + return prompter.prompt(message, null, null); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new PrompterException("Unable to prompt", e); + } + } + + @Override + public String prompt(String message, String defaultReply) throws PrompterException { + try { + return prompter.prompt(message, null, defaultReply); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new PrompterException("Unable to prompt", e); + } + } + + @Override + public String prompt(String message, List possibleValues) throws PrompterException { + try { + return prompter.prompt(message, possibleValues, null); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new PrompterException("Unable to prompt", e); + } + } + + @Override + public String prompt(String message, List possibleValues, String defaultReply) throws PrompterException { + try { + return prompter.prompt(message, possibleValues, defaultReply); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new PrompterException("Unable to prompt", e); + } + } + + @Override + public String promptForPassword(String message) throws PrompterException { + try { + return prompter.promptForPassword(message); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new PrompterException("Unable to promptForPassword", e); + } + } + + @Override + public void showMessage(String message) throws PrompterException { + try { + prompter.showMessage(message); + } catch (org.apache.maven.api.services.PrompterException e) { + throw new PrompterException("Unable to showMessage", e); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/plugin/PluginManager.java b/compat/maven-compat/src/main/java/org/apache/maven/plugin/PluginManager.java new file mode 100644 index 000000000000..2b7e44e0f742 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/plugin/PluginManager.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.plugin; + +import java.util.Map; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.version.PluginVersionNotFoundException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.artifact.InvalidDependencyVersionException; +import org.apache.maven.settings.Settings; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +/** + */ +@Deprecated +public interface PluginManager { + String ROLE = PluginManager.class.getName(); + + void executeMojo(MavenProject project, MojoExecution execution, MavenSession session) + throws MojoExecutionException, ArtifactResolutionException, MojoFailureException, ArtifactNotFoundException, + InvalidDependencyVersionException, PluginManagerException, PluginConfigurationException; + + PluginDescriptor getPluginDescriptorForPrefix(String prefix); + + Plugin getPluginDefinitionForPrefix(String prefix, MavenSession session, MavenProject project); + + PluginDescriptor verifyPlugin( + Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository) + throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException, + InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, + PluginNotFoundException, PluginVersionNotFoundException; + + Object getPluginComponent(Plugin plugin, String role, String roleHint) + throws PluginManagerException, ComponentLookupException; + + Map getPluginComponents(Plugin plugin, String role) + throws ComponentLookupException, PluginManagerException; + + /** + * @since 2.2.1 + */ + PluginDescriptor loadPluginDescriptor(Plugin plugin, MavenProject project, MavenSession session) + throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException, + InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, + PluginNotFoundException, PluginVersionNotFoundException; + + /** + * @since 2.2.1 + */ + PluginDescriptor loadPluginFully(Plugin plugin, MavenProject project, MavenSession session) + throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException, + InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, + PluginNotFoundException, PluginVersionNotFoundException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java b/compat/maven-compat/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java new file mode 100644 index 000000000000..fe4891cd2ed5 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.plugin.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.Map; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.InvalidPluginException; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.plugin.MavenPluginManager; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.PluginConfigurationException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginManager; +import org.apache.maven.plugin.PluginManagerException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.prefix.DefaultPluginPrefixRequest; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.prefix.PluginPrefixRequest; +import org.apache.maven.plugin.prefix.PluginPrefixResolver; +import org.apache.maven.plugin.prefix.PluginPrefixResult; +import org.apache.maven.plugin.version.DefaultPluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionNotFoundException; +import org.apache.maven.plugin.version.PluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolver; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.artifact.InvalidDependencyVersionException; +import org.apache.maven.settings.Settings; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultPluginManager implements PluginManager { + + private final PlexusContainer container; + private final MavenPluginManager pluginManager; + private final PluginVersionResolver pluginVersionResolver; + private final PluginPrefixResolver pluginPrefixResolver; + private final LegacySupport legacySupport; + + @Inject + public DefaultPluginManager( + PlexusContainer container, + MavenPluginManager pluginManager, + PluginVersionResolver pluginVersionResolver, + PluginPrefixResolver pluginPrefixResolver, + LegacySupport legacySupport) { + this.container = container; + this.pluginManager = pluginManager; + this.pluginVersionResolver = pluginVersionResolver; + this.pluginPrefixResolver = pluginPrefixResolver; + this.legacySupport = legacySupport; + } + + @Override + public void executeMojo(MavenProject project, MojoExecution execution, MavenSession session) + throws MojoExecutionException, ArtifactResolutionException, MojoFailureException, ArtifactNotFoundException, + InvalidDependencyVersionException, PluginManagerException, PluginConfigurationException { + throw new UnsupportedOperationException(); + } + + @Override + public Object getPluginComponent(Plugin plugin, String role, String roleHint) + throws PluginManagerException, ComponentLookupException { + MavenSession session = legacySupport.getSession(); + + PluginDescriptor pluginDescriptor; + try { + pluginDescriptor = pluginManager.getPluginDescriptor( + plugin, session.getCurrentProject().getRemotePluginRepositories(), session.getRepositorySession()); + + pluginManager.setupPluginRealm(pluginDescriptor, session, null, null, null); + } catch (Exception e) { + throw new PluginManagerException(plugin, e.getMessage(), e); + } + + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(pluginDescriptor.getClassRealm()); + + return container.lookup(role, roleHint); + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + + @Override + public Map getPluginComponents(Plugin plugin, String role) + throws ComponentLookupException, PluginManagerException { + MavenSession session = legacySupport.getSession(); + + PluginDescriptor pluginDescriptor; + try { + pluginDescriptor = pluginManager.getPluginDescriptor( + plugin, session.getCurrentProject().getRemotePluginRepositories(), session.getRepositorySession()); + + pluginManager.setupPluginRealm(pluginDescriptor, session, null, null, null); + } catch (Exception e) { + throw new PluginManagerException(plugin, e.getMessage(), e); + } + + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(pluginDescriptor.getClassRealm()); + + return container.lookupMap(role); + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + + @Override + public Plugin getPluginDefinitionForPrefix(String prefix, MavenSession session, MavenProject project) { + PluginPrefixRequest request = new DefaultPluginPrefixRequest(prefix, session); + request.setPom(project.getModel()); + + try { + PluginPrefixResult result = pluginPrefixResolver.resolve(request); + + Plugin plugin = new Plugin(); + plugin.setGroupId(result.getGroupId()); + plugin.setArtifactId(result.getArtifactId()); + + return plugin; + } catch (NoPluginFoundForPrefixException e) { + return null; + } + } + + @Override + public PluginDescriptor getPluginDescriptorForPrefix(String prefix) { + MavenSession session = legacySupport.getSession(); + + PluginPrefixRequest request = new DefaultPluginPrefixRequest(prefix, session); + + try { + PluginPrefixResult result = pluginPrefixResolver.resolve(request); + + Plugin plugin = new Plugin(); + plugin.setGroupId(result.getGroupId()); + plugin.setArtifactId(result.getArtifactId()); + + return loadPluginDescriptor(plugin, session.getCurrentProject(), session); + } catch (Exception e) { + return null; + } + } + + @Override + public PluginDescriptor loadPluginDescriptor(Plugin plugin, MavenProject project, MavenSession session) + throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException, + InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, + PluginNotFoundException, PluginVersionNotFoundException { + return verifyPlugin(plugin, project, session.getSettings(), session.getLocalRepository()); + } + + @Override + public PluginDescriptor loadPluginFully(Plugin plugin, MavenProject project, MavenSession session) + throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException, + InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, + PluginNotFoundException, PluginVersionNotFoundException { + PluginDescriptor pluginDescriptor = loadPluginDescriptor(plugin, project, session); + + try { + pluginManager.setupPluginRealm(pluginDescriptor, session, null, null, null); + } catch (PluginResolutionException e) { + throw new PluginManagerException(plugin, e.getMessage(), e); + } + + return pluginDescriptor; + } + + @Override + public PluginDescriptor verifyPlugin( + Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository) + throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException, + InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, + PluginNotFoundException, PluginVersionNotFoundException { + MavenSession session = legacySupport.getSession(); + + if (plugin.getVersion() == null) { + PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( + plugin, session.getRepositorySession(), project.getRemotePluginRepositories()); + plugin.setVersion(pluginVersionResolver.resolve(versionRequest).getVersion()); + } + + try { + return pluginManager.getPluginDescriptor( + plugin, project.getRemotePluginRepositories(), session.getRepositorySession()); + } catch (PluginResolutionException e) { + throw new PluginNotFoundException(plugin, project.getPluginArtifactRepositories()); + } catch (PluginDescriptorParsingException | InvalidPluginDescriptorException e) { + throw new PluginManagerException(plugin, e.getMessage(), e); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/DefaultProfileManager.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/DefaultProfileManager.java new file mode 100644 index 000000000000..6960d8dded11 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/DefaultProfileManager.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles; + +import javax.inject.Inject; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.Profile; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.profile.DefaultProfileActivationContext; +import org.apache.maven.model.profile.ProfileSelector; +import org.apache.maven.profiles.activation.ProfileActivationException; +import org.codehaus.plexus.MutablePlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.logging.Logger; + +/** + * DefaultProfileManager + */ +@Deprecated +public class DefaultProfileManager implements ProfileManager { + + @Inject + private Logger logger; + + @Inject + private ProfileSelector profileSelector; + + private List activatedIds = new ArrayList<>(); + + private List deactivatedIds = new ArrayList<>(); + + private List defaultIds = new ArrayList<>(); + + private Map profilesById = new LinkedHashMap<>(); + + private Properties requestProperties; + + /** + * @deprecated without passing in the system properties, the SystemPropertiesProfileActivator will not work + * correctly in embedded environments. + */ + @Deprecated + public DefaultProfileManager(PlexusContainer container) { + this(container, null); + } + + /** + * the properties passed to the profile manager are the props that + * are passed to maven, possibly containing profile activator properties + * + */ + public DefaultProfileManager(PlexusContainer container, Properties props) { + try { + this.profileSelector = container.lookup(ProfileSelector.class); + this.logger = ((MutablePlexusContainer) container).getLogger(); + } catch (ComponentLookupException e) { + throw new IllegalStateException(e); + } + this.requestProperties = props; + } + + @Override + public Properties getRequestProperties() { + return requestProperties; + } + + @Override + public Map getProfilesById() { + return profilesById; + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#addProfile(org.apache.maven.model.Profile) + */ + @Override + public void addProfile(Profile profile) { + String profileId = profile.getId(); + + Profile existing = profilesById.get(profileId); + if (existing != null) { + logger.warn("Overriding profile: '" + profileId + "' (source: " + existing.getSource() + + ") with new instance from source: " + profile.getSource()); + } + + profilesById.put(profile.getId(), profile); + + Activation activation = profile.getActivation(); + + if (activation != null && activation.isActiveByDefault()) { + activateAsDefault(profileId); + } + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#explicitlyActivate(java.lang.String) + */ + @Override + public void explicitlyActivate(String profileId) { + if (!activatedIds.contains(profileId)) { + logger.debug("Profile with id: '" + profileId + "' has been explicitly activated."); + + activatedIds.add(profileId); + } + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#explicitlyActivate(java.util.List) + */ + @Override + public void explicitlyActivate(List profileIds) { + for (String profileId1 : profileIds) { + explicitlyActivate(profileId1); + } + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#explicitlyDeactivate(java.lang.String) + */ + @Override + public void explicitlyDeactivate(String profileId) { + if (!deactivatedIds.contains(profileId)) { + logger.debug("Profile with id: '" + profileId + "' has been explicitly deactivated."); + + deactivatedIds.add(profileId); + } + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#explicitlyDeactivate(java.util.List) + */ + @Override + public void explicitlyDeactivate(List profileIds) { + for (String profileId1 : profileIds) { + explicitlyDeactivate(profileId1); + } + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#getActiveProfiles() + */ + @Override + public List getActiveProfiles() throws ProfileActivationException { + DefaultProfileActivationContext context = new DefaultProfileActivationContext(); + context.setActiveProfileIds(activatedIds); + context.setInactiveProfileIds(deactivatedIds); + context.setSystemProperties(System.getProperties()); + context.setUserProperties(requestProperties); + + final List errors = new ArrayList<>(); + + List profiles = profileSelector.getActiveProfiles(profilesById.values(), context, req -> { + if (!ModelProblem.Severity.WARNING.equals(req.getSeverity())) { + errors.add(new ProfileActivationException(req.getMessage(), req.getException())); + } + }); + + if (!errors.isEmpty()) { + throw errors.get(0); + } + + return profiles; + } + + /* (non-Javadoc) + * @see org.apache.maven.profiles.ProfileManager#addProfiles(java.util.List) + */ + @Override + public void addProfiles(List profiles) { + for (Profile profile1 : profiles) { + addProfile(profile1); + } + } + + public void activateAsDefault(String profileId) { + if (!defaultIds.contains(profileId)) { + defaultIds.add(profileId); + } + } + + @Override + public List getExplicitlyActivatedIds() { + return activatedIds; + } + + @Override + public List getExplicitlyDeactivatedIds() { + return deactivatedIds; + } + + @Override + public List getIdsActivatedByDefault() { + return defaultIds; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/ProfileManager.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/ProfileManager.java new file mode 100644 index 000000000000..3781be1c69fb --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/ProfileManager.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.model.Profile; +import org.apache.maven.profiles.activation.ProfileActivationException; + +/** + * ProfileManager + */ +@Deprecated +public interface ProfileManager { + + void addProfile(Profile profile); + + void explicitlyActivate(String profileId); + + void explicitlyActivate(List profileIds); + + void explicitlyDeactivate(String profileId); + + void explicitlyDeactivate(List profileIds); + + List getActiveProfiles() throws ProfileActivationException; + + void addProfiles(List profiles); + + Map getProfilesById(); + + List getExplicitlyActivatedIds(); + + List getExplicitlyDeactivatedIds(); + + List getIdsActivatedByDefault(); + + Properties getRequestProperties(); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/ProfilesConversionUtils.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/ProfilesConversionUtils.java new file mode 100644 index 000000000000..e0e77d2800cc --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/ProfilesConversionUtils.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles; + +import java.util.List; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationFile; +import org.apache.maven.model.ActivationProperty; +import org.apache.maven.model.Profile; +import org.apache.maven.model.Repository; + +/** + * ProfilesConversionUtils + */ +@Deprecated +public class ProfilesConversionUtils { + private ProfilesConversionUtils() {} + + public static Profile convertFromProfileXmlProfile(org.apache.maven.profiles.Profile profileXmlProfile) { + Profile profile = new Profile(); + + profile.setId(profileXmlProfile.getId()); + + profile.setSource("profiles.xml"); + + org.apache.maven.profiles.Activation profileActivation = profileXmlProfile.getActivation(); + + if (profileActivation != null) { + Activation activation = new Activation(); + + activation.setActiveByDefault(profileActivation.isActiveByDefault()); + + activation.setJdk(profileActivation.getJdk()); + + org.apache.maven.profiles.ActivationProperty profileProp = profileActivation.getProperty(); + + if (profileProp != null) { + ActivationProperty prop = new ActivationProperty(); + + prop.setName(profileProp.getName()); + prop.setValue(profileProp.getValue()); + + activation.setProperty(prop); + } + + ActivationOS profileOs = profileActivation.getOs(); + + if (profileOs != null) { + org.apache.maven.model.ActivationOS os = new org.apache.maven.model.ActivationOS(); + + os.setArch(profileOs.getArch()); + os.setFamily(profileOs.getFamily()); + os.setName(profileOs.getName()); + os.setVersion(profileOs.getVersion()); + + activation.setOs(os); + } + + org.apache.maven.profiles.ActivationFile profileFile = profileActivation.getFile(); + + if (profileFile != null) { + ActivationFile file = new ActivationFile(); + + file.setExists(profileFile.getExists()); + file.setMissing(profileFile.getMissing()); + + activation.setFile(file); + } + + profile.setActivation(activation); + } + + profile.setProperties(profileXmlProfile.getProperties()); + + List repos = profileXmlProfile.getRepositories(); + if (repos != null) { + for (Object repo : repos) { + profile.addRepository(convertFromProfileXmlRepository((org.apache.maven.profiles.Repository) repo)); + } + } + + List pluginRepos = profileXmlProfile.getPluginRepositories(); + if (pluginRepos != null) { + for (Object pluginRepo : pluginRepos) { + profile.addPluginRepository( + convertFromProfileXmlRepository((org.apache.maven.profiles.Repository) pluginRepo)); + } + } + + return profile; + } + + private static Repository convertFromProfileXmlRepository(org.apache.maven.profiles.Repository profileXmlRepo) { + Repository repo = new Repository(); + + repo.setId(profileXmlRepo.getId()); + repo.setLayout(profileXmlRepo.getLayout()); + repo.setName(profileXmlRepo.getName()); + repo.setUrl(profileXmlRepo.getUrl()); + + if (profileXmlRepo.getSnapshots() != null) { + repo.setSnapshots(convertRepositoryPolicy(profileXmlRepo.getSnapshots())); + } + if (profileXmlRepo.getReleases() != null) { + repo.setReleases(convertRepositoryPolicy(profileXmlRepo.getReleases())); + } + + return repo; + } + + private static org.apache.maven.model.RepositoryPolicy convertRepositoryPolicy(RepositoryPolicy profileXmlRepo) { + org.apache.maven.model.RepositoryPolicy policy = new org.apache.maven.model.RepositoryPolicy(); + policy.setEnabled(profileXmlRepo.isEnabled()); + policy.setUpdatePolicy(profileXmlRepo.getUpdatePolicy()); + policy.setChecksumPolicy(profileXmlRepo.getChecksumPolicy()); + return policy; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/DetectedProfileActivator.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/DetectedProfileActivator.java new file mode 100644 index 000000000000..5afd5788c606 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/DetectedProfileActivator.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.activation; + +import org.apache.maven.model.Profile; + +/** + * DetectedProfileActivator + */ +@Deprecated +public abstract class DetectedProfileActivator implements ProfileActivator { + @Override + public boolean canDetermineActivation(Profile profile) { + return canDetectActivation(profile); + } + + protected abstract boolean canDetectActivation(Profile profile); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/FileProfileActivator.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/FileProfileActivator.java new file mode 100644 index 000000000000..8a1b82a8e99c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/FileProfileActivator.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.activation; + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationFile; +import org.apache.maven.model.Profile; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; + +/** + * FileProfileActivator + */ +@Deprecated +public class FileProfileActivator extends DetectedProfileActivator implements LogEnabled { + private Logger logger; + + @Override + protected boolean canDetectActivation(Profile profile) { + return profile.getActivation() != null && profile.getActivation().getFile() != null; + } + + @Override + public boolean isActive(Profile profile) { + Activation activation = profile.getActivation(); + + ActivationFile actFile = activation.getFile(); + + if (actFile != null) { + // check if the file exists, if it does then the profile will be active + String fileString = actFile.getExists(); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + try { + interpolator.addValueSource(new EnvarBasedValueSource()); + } catch (IOException e) { + // ignored + } + interpolator.addValueSource(new MapBasedValueSource(System.getProperties())); + + try { + if (fileString != null && !fileString.isEmpty()) { + fileString = interpolator.interpolate(fileString, "").replace("\\", "/"); + File file = new File(fileString); + return file.exists(); + } + + // check if the file is missing, if it is then the profile will be active + fileString = actFile.getMissing(); + + if (fileString != null && !fileString.isEmpty()) { + fileString = interpolator.interpolate(fileString, "").replace("\\", "/"); + File file = new File(fileString); + return !file.exists(); + } + } catch (InterpolationException e) { + if (logger.isDebugEnabled()) { + logger.debug("Failed to interpolate missing file location for profile activator: " + fileString, e); + } else { + logger.warn("Failed to interpolate missing file location for profile activator: " + fileString + + ", enable verbose output (-X) for more details"); + } + } + } + + return false; + } + + @Override + public void enableLogging(Logger logger) { + this.logger = logger; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/JdkPrefixProfileActivator.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/JdkPrefixProfileActivator.java new file mode 100644 index 000000000000..223a33c00d8c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/JdkPrefixProfileActivator.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.activation; + +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.model.Activation; +import org.apache.maven.model.Profile; + +/** + * JdkPrefixProfileActivator + */ +@Deprecated +public class JdkPrefixProfileActivator extends DetectedProfileActivator { + private static final String JDK_VERSION = System.getProperty("java.version"); + + @Override + public boolean isActive(Profile profile) throws ProfileActivationException { + Activation activation = profile.getActivation(); + + String jdk = activation.getJdk(); + + // null case is covered by canDetermineActivation(), so we can do a straight startsWith() here. + if (jdk.startsWith("[") || jdk.startsWith("(")) { + try { + return matchJdkVersionRange(jdk); + } catch (InvalidVersionSpecificationException e) { + throw new ProfileActivationException( + "Invalid JDK version in profile '" + profile.getId() + "': " + e.getMessage()); + } + } + + boolean reverse = false; + + if (jdk.startsWith("!")) { + reverse = true; + jdk = jdk.substring(1); + } + + if (getJdkVersion().startsWith(jdk)) { + return !reverse; + } else { + return reverse; + } + } + + private boolean matchJdkVersionRange(String jdk) throws InvalidVersionSpecificationException { + VersionRange jdkVersionRange = VersionRange.createFromVersionSpec(convertJdkToMavenVersion(jdk)); + DefaultArtifactVersion jdkVersion = new DefaultArtifactVersion(convertJdkToMavenVersion(getJdkVersion())); + return jdkVersionRange.containsVersion(jdkVersion); + } + + private String convertJdkToMavenVersion(String jdk) { + return jdk.replace("_", "-"); + } + + protected String getJdkVersion() { + return JDK_VERSION; + } + + @Override + protected boolean canDetectActivation(Profile profile) { + return profile.getActivation() != null + && profile.getActivation().getJdk() != null + && !profile.getActivation().getJdk().isEmpty(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/OperatingSystemProfileActivator.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/OperatingSystemProfileActivator.java new file mode 100644 index 000000000000..0c7944b53992 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/OperatingSystemProfileActivator.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.activation; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationOS; +import org.apache.maven.model.Profile; +import org.apache.maven.utils.Os; + +/** + * OperatingSystemProfileActivator + */ +@Deprecated +public class OperatingSystemProfileActivator implements ProfileActivator { + + @Override + public boolean canDetermineActivation(Profile profile) { + Activation activation = profile.getActivation(); + return activation != null && activation.getOs() != null; + } + + @Override + public boolean isActive(Profile profile) { + Activation activation = profile.getActivation(); + ActivationOS os = activation.getOs(); + + boolean result = ensureAtLeastOneNonNull(os); + + if (result && os.getFamily() != null) { + result = determineFamilyMatch(os.getFamily()); + } + if (result && os.getName() != null) { + result = determineNameMatch(os.getName()); + } + if (result && os.getArch() != null) { + result = determineArchMatch(os.getArch()); + } + if (result && os.getVersion() != null) { + result = determineVersionMatch(os.getVersion()); + } + return result; + } + + private boolean ensureAtLeastOneNonNull(ActivationOS os) { + return os.getArch() != null || os.getFamily() != null || os.getName() != null || os.getVersion() != null; + } + + private boolean determineVersionMatch(String version) { + String test = version; + boolean reverse = false; + + if (test.startsWith("!")) { + reverse = true; + test = test.substring(1); + } + + boolean result = Os.OS_VERSION.equals(test); + + if (reverse) { + return !result; + } else { + return result; + } + } + + private boolean determineArchMatch(String arch) { + String test = arch; + boolean reverse = false; + + if (test.startsWith("!")) { + reverse = true; + test = test.substring(1); + } + + boolean result = Os.OS_ARCH.equals(test); + + if (reverse) { + return !result; + } else { + return result; + } + } + + private boolean determineNameMatch(String name) { + String test = name; + boolean reverse = false; + + if (test.startsWith("!")) { + reverse = true; + test = test.substring(1); + } + + boolean result = Os.OS_NAME.equals(test); + + if (reverse) { + return !result; + } else { + return result; + } + } + + private boolean determineFamilyMatch(String family) { + String test = family; + boolean reverse = false; + + if (test.startsWith("!")) { + reverse = true; + test = test.substring(1); + } + + boolean result = Os.isFamily(test); + + if (reverse) { + return !result; + } else { + return result; + } + } +} diff --git a/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivationException.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivationException.java similarity index 75% rename from maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivationException.java rename to compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivationException.java index 59f70bed0e4e..5215d9e19178 100644 --- a/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivationException.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivationException.java @@ -1,5 +1,3 @@ -package org.apache.maven.profiles.activation; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,25 +16,21 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.profiles.activation; /** * ProfileActivationException */ @Deprecated -public class ProfileActivationException - extends Exception -{ +public class ProfileActivationException extends Exception { private static final long serialVersionUID = -90820222109103638L; - public ProfileActivationException( String message, Throwable cause ) - { - super( message, cause ); + public ProfileActivationException(String message, Throwable cause) { + super(message, cause); } - public ProfileActivationException( String message ) - { - super( message ); + public ProfileActivationException(String message) { + super(message); } - } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivator.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivator.java new file mode 100644 index 000000000000..fc8a6e7f00f5 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/ProfileActivator.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.activation; + +import org.apache.maven.model.Profile; + +/** + * ProfileActivator + */ +@Deprecated +public interface ProfileActivator { + + String ROLE = ProfileActivator.class.getName(); + + boolean canDetermineActivation(Profile profile); + + boolean isActive(Profile profile) throws ProfileActivationException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/SystemPropertyProfileActivator.java b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/SystemPropertyProfileActivator.java new file mode 100644 index 000000000000..32a29f8aedd9 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/profiles/activation/SystemPropertyProfileActivator.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.activation; + +import java.util.Properties; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationProperty; +import org.apache.maven.model.Profile; +import org.codehaus.plexus.context.Context; +import org.codehaus.plexus.context.ContextException; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; + +/** + * SystemPropertyProfileActivator + */ +@Deprecated +public class SystemPropertyProfileActivator extends DetectedProfileActivator implements Contextualizable { + private Properties properties; + + @Override + public void contextualize(Context context) throws ContextException { + properties = (Properties) context.get("SystemProperties"); + } + + @Override + protected boolean canDetectActivation(Profile profile) { + return profile.getActivation() != null && profile.getActivation().getProperty() != null; + } + + @Override + public boolean isActive(Profile profile) throws ProfileActivationException { + Activation activation = profile.getActivation(); + + ActivationProperty property = activation.getProperty(); + + if (property != null) { + String name = property.getName(); + boolean reverseName = false; + + if (name == null) { + throw new ProfileActivationException( + "The property name is required to activate the profile '" + profile.getId() + "'"); + } + + if (name.startsWith("!")) { + reverseName = true; + name = name.substring(1); + } + + String sysValue = properties.getProperty(name); + + String propValue = property.getValue(); + if (propValue != null && !propValue.isEmpty()) { + boolean reverseValue = false; + if (propValue.startsWith("!")) { + reverseValue = true; + propValue = propValue.substring(1); + } + + // we have a value, so it has to match the system value... + boolean result = propValue.equals(sysValue); + + if (reverseValue) { + return !result; + } else { + return result; + } + } else { + boolean result = sysValue != null && !sysValue.isEmpty(); + + if (reverseName) { + return !result; + } else { + return result; + } + } + } + + return false; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java b/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java new file mode 100644 index 000000000000..300b0298c4a3 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java @@ -0,0 +1,296 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Repository; +import org.apache.maven.model.building.ModelBuildingException; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.building.ModelSource; +import org.apache.maven.model.building.UrlModelSource; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.profiles.ProfileManager; +import org.apache.maven.properties.internal.EnvironmentUtils; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.wagon.events.TransferListener; + +/** + */ +@Deprecated +@Named +@Singleton +public class DefaultMavenProjectBuilder implements MavenProjectBuilder { + + @Inject + private ProjectBuilder projectBuilder; + + @Inject + private RepositorySystem repositorySystem; + + @Inject + private LegacySupport legacySupport; + + // ---------------------------------------------------------------------- + // MavenProjectBuilder Implementation + // ---------------------------------------------------------------------- + + private ProjectBuildingRequest toRequest(ProjectBuilderConfiguration configuration) { + DefaultProjectBuildingRequest request = new DefaultProjectBuildingRequest(); + + request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0); + request.setResolveDependencies(false); + + request.setLocalRepository(configuration.getLocalRepository()); + request.setBuildStartTime(configuration.getBuildStartTime()); + request.setUserProperties(configuration.getUserProperties()); + request.setSystemProperties(configuration.getExecutionProperties()); + + ProfileManager profileManager = configuration.getGlobalProfileManager(); + if (profileManager != null) { + request.setActiveProfileIds(profileManager.getExplicitlyActivatedIds()); + request.setInactiveProfileIds(profileManager.getExplicitlyDeactivatedIds()); + } else { + /* + * MNG-4900: Hack to workaround deficiency of legacy API which makes it impossible for plugins to access the + * global profile manager which is required to build a POM like a CLI invocation does. Failure to consider + * the activated profiles can cause repo declarations to be lost which in turn will result in artifact + * resolution failures, in particular when using the enhanced local repo which guards access to local files + * based on the configured remote repos. + */ + MavenSession session = legacySupport.getSession(); + if (session != null) { + MavenExecutionRequest req = session.getRequest(); + if (req != null) { + request.setActiveProfileIds(req.getActiveProfiles()); + request.setInactiveProfileIds(req.getInactiveProfiles()); + } + } + } + + return request; + } + + private ProjectBuildingRequest injectSession(ProjectBuildingRequest request) { + MavenSession session = legacySupport.getSession(); + if (session != null) { + request.setRepositorySession(session.getRepositorySession()); + request.setSystemProperties(session.getSystemProperties()); + if (request.getUserProperties().isEmpty()) { + request.setUserProperties(session.getUserProperties()); + } + + MavenExecutionRequest req = session.getRequest(); + if (req != null) { + request.setRemoteRepositories(req.getRemoteRepositories()); + } + } else { + Properties props = new Properties(); + EnvironmentUtils.addEnvVars(props); + props.putAll(System.getProperties()); + request.setSystemProperties(props); + } + + return request; + } + + @SuppressWarnings("unchecked") + private List normalizeToArtifactRepositories( + List repositories, ProjectBuildingRequest request) throws ProjectBuildingException { + /* + * This provides backward-compat with 2.x that allowed plugins like the maven-remote-resources-plugin:1.0 to + * populate the builder configuration with model repositories instead of artifact repositories. + */ + + if (repositories != null) { + boolean normalized = false; + + List repos = new ArrayList<>(repositories.size()); + + for (Object repository : repositories) { + if (repository instanceof Repository repositoryInstance) { + try { + ArtifactRepository repo = repositorySystem.buildArtifactRepository(repositoryInstance); + repositorySystem.injectMirror(request.getRepositorySession(), Arrays.asList(repo)); + repositorySystem.injectProxy(request.getRepositorySession(), Arrays.asList(repo)); + repositorySystem.injectAuthentication(request.getRepositorySession(), Arrays.asList(repo)); + repos.add(repo); + } catch (InvalidRepositoryException e) { + throw new ProjectBuildingException("", "Invalid remote repository " + repository, e); + } + normalized = true; + } else { + repos.add((ArtifactRepository) repository); + } + } + + if (normalized) { + return repos; + } + } + + return (List) repositories; + } + + private ProjectBuildingException transformError(ProjectBuildingException e) { + if (e.getCause() instanceof ModelBuildingException) { + return new InvalidProjectModelException(e.getProjectId(), e.getMessage(), e.getPomFile()); + } + + return e; + } + + @Override + public MavenProject build(File pom, ProjectBuilderConfiguration configuration) throws ProjectBuildingException { + ProjectBuildingRequest request = injectSession(toRequest(configuration)); + + try { + return projectBuilder.build(pom, request).getProject(); + } catch (ProjectBuildingException e) { + throw transformError(e); + } + } + + // This is used by the SITE plugin. + @Override + public MavenProject build(File pom, ArtifactRepository localRepository, ProfileManager profileManager) + throws ProjectBuildingException { + ProjectBuilderConfiguration configuration = new DefaultProjectBuilderConfiguration(); + configuration.setLocalRepository(localRepository); + configuration.setGlobalProfileManager(profileManager); + + return build(pom, configuration); + } + + public MavenProject buildFromRepository( + Artifact artifact, + List remoteRepositories, + ProjectBuilderConfiguration configuration, + boolean allowStubModel) + throws ProjectBuildingException { + ProjectBuildingRequest request = injectSession(toRequest(configuration)); + request.setRemoteRepositories(normalizeToArtifactRepositories(remoteRepositories, request)); + request.setProcessPlugins(false); + request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + + try { + return projectBuilder.build(artifact, allowStubModel, request).getProject(); + } catch (ProjectBuildingException e) { + throw transformError(e); + } + } + + @Override + public MavenProject buildFromRepository( + Artifact artifact, + List remoteRepositories, + ArtifactRepository localRepository, + boolean allowStubModel) + throws ProjectBuildingException { + ProjectBuilderConfiguration configuration = new DefaultProjectBuilderConfiguration(); + configuration.setLocalRepository(localRepository); + + return buildFromRepository(artifact, remoteRepositories, configuration, allowStubModel); + } + + @Override + public MavenProject buildFromRepository( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ProjectBuildingException { + return buildFromRepository(artifact, remoteRepositories, localRepository, true); + } + + /** + * This is used for pom-less execution like running archetype:generate. I am taking out the profile handling and the + * interpolation of the base directory until we spec this out properly. + */ + @Override + public MavenProject buildStandaloneSuperProject(ProjectBuilderConfiguration configuration) + throws ProjectBuildingException { + ProjectBuildingRequest request = injectSession(toRequest(configuration)); + request.setProcessPlugins(false); + request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + + ModelSource modelSource = new UrlModelSource(getClass().getResource("standalone.xml")); + + MavenProject project = projectBuilder.build(modelSource, request).getProject(); + project.setExecutionRoot(true); + return project; + } + + @Override + public MavenProject buildStandaloneSuperProject(ArtifactRepository localRepository) + throws ProjectBuildingException { + return buildStandaloneSuperProject(localRepository, null); + } + + @Override + public MavenProject buildStandaloneSuperProject(ArtifactRepository localRepository, ProfileManager profileManager) + throws ProjectBuildingException { + ProjectBuilderConfiguration configuration = new DefaultProjectBuilderConfiguration(); + configuration.setLocalRepository(localRepository); + configuration.setGlobalProfileManager(profileManager); + + return buildStandaloneSuperProject(configuration); + } + + @Override + public MavenProject buildWithDependencies( + File pom, + ArtifactRepository localRepository, + ProfileManager profileManager, + TransferListener transferListener) + throws ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException { + ProjectBuilderConfiguration configuration = new DefaultProjectBuilderConfiguration(); + configuration.setLocalRepository(localRepository); + configuration.setGlobalProfileManager(profileManager); + + ProjectBuildingRequest request = injectSession(toRequest(configuration)); + + request.setResolveDependencies(true); + + try { + return projectBuilder.build(pom, request).getProject(); + } catch (ProjectBuildingException e) { + throw transformError(e); + } + } + + @Override + public MavenProject buildWithDependencies( + File pom, ArtifactRepository localRepository, ProfileManager profileManager) + throws ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException { + return buildWithDependencies(pom, localRepository, profileManager, null); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultProjectBuilderConfiguration.java b/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultProjectBuilderConfiguration.java new file mode 100644 index 000000000000..3fc47cd72074 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultProjectBuilderConfiguration.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.util.Date; +import java.util.Properties; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.profiles.ProfileManager; + +/** + * DefaultProjectBuilderConfiguration + */ +@Deprecated +public class DefaultProjectBuilderConfiguration implements ProjectBuilderConfiguration { + + private ProfileManager globalProfileManager; + + private ArtifactRepository localRepository; + + private Properties userProperties; + + private Properties executionProperties = System.getProperties(); + + private Date buildStartTime; + + public DefaultProjectBuilderConfiguration() {} + + @Override + public ProjectBuilderConfiguration setGlobalProfileManager(ProfileManager globalProfileManager) { + this.globalProfileManager = globalProfileManager; + return this; + } + + @Override + public ProfileManager getGlobalProfileManager() { + return globalProfileManager; + } + + @Override + public ProjectBuilderConfiguration setLocalRepository(ArtifactRepository localRepository) { + this.localRepository = localRepository; + return this; + } + + @Override + public ArtifactRepository getLocalRepository() { + return localRepository; + } + + @Override + public ProjectBuilderConfiguration setUserProperties(Properties userProperties) { + this.userProperties = userProperties; + return this; + } + + @Override + public Properties getUserProperties() { + if (userProperties == null) { + userProperties = new Properties(); + } + + return userProperties; + } + + @Override + public Properties getExecutionProperties() { + return executionProperties; + } + + @Override + public ProjectBuilderConfiguration setExecutionProperties(Properties executionProperties) { + this.executionProperties = executionProperties; + return this; + } + + @Override + public Date getBuildStartTime() { + return buildStartTime; + } + + @Override + public ProjectBuilderConfiguration setBuildStartTime(Date buildStartTime) { + this.buildStartTime = buildStartTime; + return this; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/InvalidProjectModelException.java b/compat/maven-compat/src/main/java/org/apache/maven/project/InvalidProjectModelException.java new file mode 100644 index 000000000000..1b9e98fb3acf --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/InvalidProjectModelException.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.io.File; + +import org.apache.maven.project.validation.ModelValidationResult; + +/** + * InvalidProjectModelException + */ +@Deprecated +public class InvalidProjectModelException extends ProjectBuildingException { + private ModelValidationResult validationResult; + + public InvalidProjectModelException(String projectId, String message, File pomLocation) { + super(projectId, message, pomLocation); + } + + /** + * @param projectId + * @param pomLocation absolute path of the pom file + * @param message + * @param validationResult + * @deprecated use {@link File} constructor for pomLocation + */ + @Deprecated + public InvalidProjectModelException( + String projectId, String pomLocation, String message, ModelValidationResult validationResult) { + this(projectId, message, new File(pomLocation), validationResult); + } + + public InvalidProjectModelException( + String projectId, String message, File pomFile, ModelValidationResult validationResult) { + super(projectId, message, pomFile); + + this.validationResult = validationResult; + } + + /** + * @param projectId + * @param pomLocation absolute path of the pom file + * @param message + * @deprecated use {@link File} constructor for pomLocation + */ + @Deprecated + public InvalidProjectModelException(String projectId, String pomLocation, String message) { + this(projectId, message, new File(pomLocation)); + } + + public final ModelValidationResult getValidationResult() { + return validationResult; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java b/compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java new file mode 100644 index 000000000000..c471a5c95bc1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.io.File; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.profiles.ProfileManager; +import org.apache.maven.wagon.events.TransferListener; + +/** + * @deprecated use {@link ProjectBuilder} instead + */ +@Deprecated +public interface MavenProjectBuilder { + + MavenProject build(File pom, ProjectBuilderConfiguration configuration) throws ProjectBuildingException; + + // TODO maven-site-plugin -- not used by the plugin directly, but used by Doxia Integration Tool & MPIR + // see DOXIASITETOOLS-167 & MPIR-349 + MavenProject build(File pom, ArtifactRepository localRepository, ProfileManager profileManager) + throws ProjectBuildingException; + + // TODO remote-resources-plugin + MavenProject buildFromRepository( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ProjectBuildingException; + + // TODO remote-resources-plugin + MavenProject buildFromRepository( + Artifact artifact, + List remoteRepositories, + ArtifactRepository localRepository, + boolean allowStubModel) + throws ProjectBuildingException; + + // TODO this is only to provide a project for plugins that don't need a project to execute but need some + // of the values from a MavenProject. Ideally this should be something internal and nothing outside Maven + // would ever need this so it should not be exposed in a public API + MavenProject buildStandaloneSuperProject(ProjectBuilderConfiguration configuration) throws ProjectBuildingException; + + MavenProject buildStandaloneSuperProject(ArtifactRepository localRepository) throws ProjectBuildingException; + + MavenProject buildStandaloneSuperProject(ArtifactRepository localRepository, ProfileManager profileManager) + throws ProjectBuildingException; + + MavenProject buildWithDependencies( + File pom, + ArtifactRepository localRepository, + ProfileManager globalProfileManager, + TransferListener transferListener) + throws ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException; + + MavenProject buildWithDependencies( + File pom, ArtifactRepository localRepository, ProfileManager globalProfileManager) + throws ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/MissingRepositoryElementException.java b/compat/maven-compat/src/main/java/org/apache/maven/project/MissingRepositoryElementException.java new file mode 100644 index 000000000000..d3a1246ecef0 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/MissingRepositoryElementException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import org.apache.maven.artifact.InvalidRepositoryException; + +/** + * Error constructing an artifact repository. + */ +@Deprecated +public class MissingRepositoryElementException extends InvalidRepositoryException { + + public MissingRepositoryElementException(String message, String repositoryId) { + super(message, repositoryId); + } + + public MissingRepositoryElementException(String message) { + super(message, "-unknown-"); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/ModelUtils.java b/compat/maven-compat/src/main/java/org/apache/maven/project/ModelUtils.java new file mode 100644 index 000000000000..baf054a48a2a --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/ModelUtils.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.util.List; + +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginContainer; + +/** @deprecated */ +@Deprecated +public final class ModelUtils { + + /** + * This should be the resulting ordering of plugins after merging: + *

    + * Given: + *

    +     * parent: X -> A -> B -> D -> E
    +     * child: Y -> A -> C -> D -> F
    +     * 
    + * Result: + *
    +     * X -> Y -> A -> B -> C -> D -> E -> F
    +     * 
    + */ + public static void mergePluginLists( + PluginContainer childContainer, PluginContainer parentContainer, boolean handleAsInheritance) { + throw new UnsupportedOperationException(); + } + + public static List orderAfterMerge( + List merged, List highPrioritySource, List lowPrioritySource) { + throw new UnsupportedOperationException(); + } + + public static void mergePluginDefinitions(Plugin child, Plugin parent, boolean handleAsInheritance) { + throw new UnsupportedOperationException(); + } + + public static void mergeFilterLists(List childFilters, List parentFilters) { + throw new UnsupportedOperationException(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/ProjectBuilderConfiguration.java b/compat/maven-compat/src/main/java/org/apache/maven/project/ProjectBuilderConfiguration.java new file mode 100644 index 000000000000..e4b98e12194e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/ProjectBuilderConfiguration.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.util.Date; +import java.util.Properties; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.profiles.ProfileManager; + +/** + * @deprecated use {@link ProjectBuildingRequest} instead + */ +@Deprecated +public interface ProjectBuilderConfiguration { + + ArtifactRepository getLocalRepository(); + + ProfileManager getGlobalProfileManager(); + + Properties getUserProperties(); + + Properties getExecutionProperties(); + + ProjectBuilderConfiguration setGlobalProfileManager(ProfileManager globalProfileManager); + + ProjectBuilderConfiguration setLocalRepository(ArtifactRepository localRepository); + + ProjectBuilderConfiguration setUserProperties(Properties userProperties); + + ProjectBuilderConfiguration setExecutionProperties(Properties executionProperties); + + Date getBuildStartTime(); + + ProjectBuilderConfiguration setBuildStartTime(Date buildStartTime); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/ProjectUtils.java b/compat/maven-compat/src/main/java/org/apache/maven/project/ProjectUtils.java new file mode 100644 index 000000000000..15c2158856d2 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/ProjectUtils.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; +import org.apache.maven.model.DeploymentRepository; +import org.apache.maven.model.Repository; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.RepositorySystem; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.eclipse.aether.RepositorySystemSession; + +// This class needs to stick around because it was exposed the remote resources plugin started using it instead of +// getting the repositories from the project. + +/** + * ProjectUtils + */ +@Deprecated +public final class ProjectUtils { + + private ProjectUtils() {} + + public static List buildArtifactRepositories( + List repositories, ArtifactRepositoryFactory artifactRepositoryFactory, PlexusContainer c) + throws InvalidRepositoryException { + + List remoteRepositories = new ArrayList<>(); + + for (Repository r : repositories) { + remoteRepositories.add(buildArtifactRepository(r, artifactRepositoryFactory, c)); + } + + return remoteRepositories; + } + + public static ArtifactRepository buildDeploymentArtifactRepository( + DeploymentRepository repo, ArtifactRepositoryFactory artifactRepositoryFactory, PlexusContainer c) + throws InvalidRepositoryException { + return buildArtifactRepository(repo, artifactRepositoryFactory, c); + } + + public static ArtifactRepository buildArtifactRepository( + Repository repo, ArtifactRepositoryFactory artifactRepositoryFactory, PlexusContainer c) + throws InvalidRepositoryException { + RepositorySystem repositorySystem = rs(c); + RepositorySystemSession session = rss(c); + + ArtifactRepository repository = repositorySystem.buildArtifactRepository(repo); + + if (session != null) { + repositorySystem.injectMirror(session, Arrays.asList(repository)); + repositorySystem.injectProxy(session, Arrays.asList(repository)); + repositorySystem.injectAuthentication(session, Arrays.asList(repository)); + } + + return repository; + } + + private static RepositorySystem rs(PlexusContainer c) { + try { + return c.lookup(RepositorySystem.class); + } catch (ComponentLookupException e) { + throw new IllegalStateException(e); + } + } + + private static RepositorySystemSession rss(PlexusContainer c) { + try { + LegacySupport legacySupport = c.lookup(LegacySupport.class); + + return legacySupport.getRepositorySession(); + } catch (ComponentLookupException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/ActiveProjectArtifact.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/ActiveProjectArtifact.java new file mode 100644 index 000000000000..19c5b64dc046 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/ActiveProjectArtifact.java @@ -0,0 +1,381 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import java.io.File; +import java.util.Collection; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.project.MavenProject; + +/** + * Wraps an active project instance to be able to receive updates from its artifact without affecting the original + * attributes of this artifact. + * + * TODO I think this exposes a design flaw in that the immutable and mutable parts of an artifact are in one class and + * should be split. ie scope, file, etc depend on the context of use, whereas everything else is immutable. + */ +@Deprecated +public class ActiveProjectArtifact implements Artifact { + private final Artifact artifact; + + private final MavenProject project; + + public ActiveProjectArtifact(MavenProject project, Artifact artifact) { + this.artifact = artifact; + this.project = project; + + artifact.setFile(project.getArtifact().getFile()); + artifact.setResolved(true); + } + + /** {@inheritDoc} */ + @Override + public File getFile() { + // we need to get the latest file for the project, not the artifact that was created at one point in time + return project.getArtifact().getFile(); + } + + /** {@inheritDoc} */ + @Override + public String getGroupId() { + return artifact.getGroupId(); + } + + /** {@inheritDoc} */ + @Override + public String getArtifactId() { + return artifact.getArtifactId(); + } + + /** {@inheritDoc} */ + @Override + public String getVersion() { + return artifact.getVersion(); + } + + /** {@inheritDoc} */ + @Override + public void setVersion(String version) { + artifact.setVersion(version); + } + + /** {@inheritDoc} */ + @Override + public String getScope() { + return artifact.getScope(); + } + + /** {@inheritDoc} */ + @Override + public String getType() { + return artifact.getType(); + } + + /** {@inheritDoc} */ + @Override + public String getClassifier() { + return artifact.getClassifier(); + } + + /** {@inheritDoc} */ + @Override + public boolean hasClassifier() { + return artifact.hasClassifier(); + } + + /** {@inheritDoc} */ + @Override + public void setFile(File destination) { + artifact.setFile(destination); + project.getArtifact().setFile(destination); + } + + /** {@inheritDoc} */ + @Override + public String getBaseVersion() { + return artifact.getBaseVersion(); + } + + /** {@inheritDoc} */ + @Override + public void setBaseVersion(String baseVersion) { + artifact.setBaseVersion(baseVersion); + } + + /** {@inheritDoc} */ + @Override + public String getId() { + return artifact.getId(); + } + + /** {@inheritDoc} */ + @Override + public String getDependencyConflictId() { + return artifact.getDependencyConflictId(); + } + + /** {@inheritDoc} */ + @Override + public void addMetadata(ArtifactMetadata metadata) { + artifact.addMetadata(metadata); + } + + /** {@inheritDoc} */ + @Override + public Collection getMetadataList() { + return artifact.getMetadataList(); + } + + /** {@inheritDoc} */ + @Override + public void setRepository(ArtifactRepository remoteRepository) { + artifact.setRepository(remoteRepository); + } + + /** {@inheritDoc} */ + @Override + public ArtifactRepository getRepository() { + return artifact.getRepository(); + } + + /** {@inheritDoc} */ + @Override + public void updateVersion(String version, ArtifactRepository localRepository) { + artifact.updateVersion(version, localRepository); + } + + /** {@inheritDoc} */ + @Override + public String getDownloadUrl() { + return artifact.getDownloadUrl(); + } + + /** {@inheritDoc} */ + @Override + public void setDownloadUrl(String downloadUrl) { + artifact.setDownloadUrl(downloadUrl); + } + + /** {@inheritDoc} */ + @Override + public ArtifactFilter getDependencyFilter() { + return artifact.getDependencyFilter(); + } + + /** {@inheritDoc} */ + @Override + public void setDependencyFilter(ArtifactFilter artifactFilter) { + artifact.setDependencyFilter(artifactFilter); + } + + /** {@inheritDoc} */ + @Override + public ArtifactHandler getArtifactHandler() { + return artifact.getArtifactHandler(); + } + + /** {@inheritDoc} */ + @Override + public List getDependencyTrail() { + return artifact.getDependencyTrail(); + } + + /** {@inheritDoc} */ + @Override + public void setDependencyTrail(List dependencyTrail) { + artifact.setDependencyTrail(dependencyTrail); + } + + /** {@inheritDoc} */ + @Override + public void setScope(String scope) { + artifact.setScope(scope); + } + + /** {@inheritDoc} */ + @Override + public VersionRange getVersionRange() { + return artifact.getVersionRange(); + } + + /** {@inheritDoc} */ + @Override + public void setVersionRange(VersionRange newRange) { + artifact.setVersionRange(newRange); + } + + /** {@inheritDoc} */ + @Override + public void selectVersion(String version) { + artifact.selectVersion(version); + } + + /** {@inheritDoc} */ + @Override + public void setGroupId(String groupId) { + artifact.setGroupId(groupId); + } + + /** {@inheritDoc} */ + @Override + public void setArtifactId(String artifactId) { + artifact.setArtifactId(artifactId); + } + + /** {@inheritDoc} */ + @Override + public boolean isSnapshot() { + return artifact.isSnapshot(); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(Artifact a) { + return artifact.compareTo(a); + } + + /** {@inheritDoc} */ + @Override + public void setResolved(boolean resolved) { + artifact.setResolved(resolved); + } + + /** {@inheritDoc} */ + @Override + public boolean isResolved() { + return artifact.isResolved(); + } + + /** {@inheritDoc} */ + @Override + public void setResolvedVersion(String version) { + artifact.setResolvedVersion(version); + } + + /** {@inheritDoc} */ + @Override + public void setArtifactHandler(ArtifactHandler handler) { + artifact.setArtifactHandler(handler); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "active project artifact[artifact: " + artifact + ", project: " + project + "]"; + } + + /** {@inheritDoc} */ + @Override + public boolean isRelease() { + return artifact.isRelease(); + } + + /** {@inheritDoc} */ + @Override + public void setRelease(boolean release) { + artifact.setRelease(release); + } + + /** {@inheritDoc} */ + @Override + public List getAvailableVersions() { + return artifact.getAvailableVersions(); + } + + /** {@inheritDoc} */ + @Override + public void setAvailableVersions(List versions) { + artifact.setAvailableVersions(versions); + } + + /** {@inheritDoc} */ + @Override + public boolean isOptional() { + return artifact.isOptional(); + } + + /** {@inheritDoc} */ + @Override + public ArtifactVersion getSelectedVersion() throws OverConstrainedVersionException { + return artifact.getSelectedVersion(); + } + + /** {@inheritDoc} */ + @Override + public boolean isSelectedVersionKnown() throws OverConstrainedVersionException { + return artifact.isSelectedVersionKnown(); + } + + /** {@inheritDoc} */ + @Override + public void setOptional(boolean optional) { + artifact.setOptional(optional); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + int result = 17; + + result = 37 * result + getGroupId().hashCode(); + result = 37 * result + getArtifactId().hashCode(); + result = 37 * result + getType().hashCode(); + if (getVersion() != null) { + result = 37 * result + getVersion().hashCode(); + } + result = 37 * result + (getClassifier() != null ? getClassifier().hashCode() : 0); + + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof Artifact a) { + if (!a.getGroupId().equals(getGroupId())) { + return false; + } else if (!a.getArtifactId().equals(getArtifactId())) { + return false; + } else if (!a.getVersion().equals(getVersion())) { + return false; + } else if (!a.getType().equals(getType())) { + return false; + } else { + return a.getClassifier() == null + ? getClassifier() == null + : a.getClassifier().equals(getClassifier()); + } + } else { + return false; + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/DefaultMavenMetadataCache.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/DefaultMavenMetadataCache.java new file mode 100644 index 000000000000..280a0e91d76e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/DefaultMavenMetadataCache.java @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; + +/** + * DefaultMavenMetadataCache + */ +@Named +@Singleton +@Deprecated +public class DefaultMavenMetadataCache implements MavenMetadataCache { + + protected final Map cache = new ConcurrentHashMap<>(); + + /** + * CacheKey + */ + public static class CacheKey { + private final Artifact artifact; + private final long pomHash; + private final boolean resolveManagedVersions; + private final List repositories = new ArrayList<>(); + private final int hashCode; + + public CacheKey( + Artifact artifact, + boolean resolveManagedVersions, + ArtifactRepository localRepository, + List remoteRepositories) { + File file = artifact.getFile(); + this.artifact = ArtifactUtils.copyArtifact(artifact); + if ("pom".equals(artifact.getType()) && file != null) { + pomHash = file.getPath().hashCode() + file.lastModified(); + } else { + pomHash = 0; + } + this.resolveManagedVersions = resolveManagedVersions; + this.repositories.add(localRepository); + this.repositories.addAll(remoteRepositories); + + int hash = 17; + hash = hash * 31 + artifactHashCode(artifact); + hash = hash * 31 + (resolveManagedVersions ? 1 : 2); + hash = hash * 31 + repositoriesHashCode(repositories); + this.hashCode = hash; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof CacheKey other) { + return pomHash == other.pomHash + && artifactEquals(artifact, other.artifact) + && resolveManagedVersions == other.resolveManagedVersions + && repositoriesEquals(repositories, other.repositories); + } else { + return false; + } + } + } + + private static int artifactHashCode(Artifact a) { + int result = 17; + result = 31 * result + a.getGroupId().hashCode(); + result = 31 * result + a.getArtifactId().hashCode(); + result = 31 * result + a.getType().hashCode(); + if (a.getVersion() != null) { + result = 31 * result + a.getVersion().hashCode(); + } + result = 31 * result + (a.getClassifier() != null ? a.getClassifier().hashCode() : 0); + result = 31 * result + (a.getScope() != null ? a.getScope().hashCode() : 0); + result = 31 * result + + (a.getDependencyFilter() != null ? a.getDependencyFilter().hashCode() : 0); + result = 31 * result + (a.isOptional() ? 1 : 0); + return result; + } + + private static boolean artifactEquals(Artifact a1, Artifact a2) { + if (a1 == a2) { + return true; + } + + return Objects.equals(a1.getGroupId(), a2.getGroupId()) + && Objects.equals(a1.getArtifactId(), a2.getArtifactId()) + && Objects.equals(a1.getType(), a2.getType()) + && Objects.equals(a1.getVersion(), a2.getVersion()) + && Objects.equals(a1.getClassifier(), a2.getClassifier()) + && Objects.equals(a1.getScope(), a2.getScope()) + && Objects.equals(a1.getDependencyFilter(), a2.getDependencyFilter()) + && a1.isOptional() == a2.isOptional(); + } + + private static int repositoryHashCode(ArtifactRepository repository) { + int result = 17; + result = 31 * result + (repository.getId() != null ? repository.getId().hashCode() : 0); + return result; + } + + private static int repositoriesHashCode(List repositories) { + int result = 17; + for (ArtifactRepository repository : repositories) { + result = 31 * result + repositoryHashCode(repository); + } + return result; + } + + private static boolean repositoryEquals(ArtifactRepository r1, ArtifactRepository r2) { + if (r1 == r2) { + return true; + } + + return Objects.equals(r1.getId(), r2.getId()) + && Objects.equals(r1.getUrl(), r2.getUrl()) + && repositoryPolicyEquals(r1.getReleases(), r2.getReleases()) + && repositoryPolicyEquals(r1.getSnapshots(), r2.getSnapshots()); + } + + private static boolean repositoryPolicyEquals(ArtifactRepositoryPolicy p1, ArtifactRepositoryPolicy p2) { + if (p1 == p2) { + return true; + } + + return p1.isEnabled() == p2.isEnabled() && Objects.equals(p1.getUpdatePolicy(), p2.getUpdatePolicy()); + } + + private static boolean repositoriesEquals(List r1, List r2) { + if (r1.size() != r2.size()) { + return false; + } + + for (Iterator it1 = r1.iterator(), it2 = r2.iterator(); it1.hasNext(); ) { + if (!repositoryEquals(it1.next(), it2.next())) { + return false; + } + } + + return true; + } + + /** + * CacheRecord + */ + public class CacheRecord { + private Artifact pomArtifact; + private Artifact relocatedArtifact; + private List artifacts; + private Map managedVersions; + private List remoteRepositories; + + private long length; + private long timestamp; + + CacheRecord( + Artifact pomArtifact, + Artifact relocatedArtifact, + Set artifacts, + Map managedVersions, + List remoteRepositories) { + this.pomArtifact = ArtifactUtils.copyArtifact(pomArtifact); + this.relocatedArtifact = ArtifactUtils.copyArtifactSafe(relocatedArtifact); + this.artifacts = ArtifactUtils.copyArtifacts(artifacts, new ArrayList<>()); + this.remoteRepositories = new ArrayList<>(remoteRepositories); + + this.managedVersions = managedVersions; + if (managedVersions != null) { + this.managedVersions = ArtifactUtils.copyArtifacts(managedVersions, new LinkedHashMap<>()); + } + + File pomFile = pomArtifact.getFile(); + if (pomFile != null && pomFile.canRead()) { + this.length = pomFile.length(); + this.timestamp = pomFile.lastModified(); + } else { + this.length = -1; + this.timestamp = -1; + } + } + + public Artifact getArtifact() { + return pomArtifact; + } + + public Artifact getRelocatedArtifact() { + return relocatedArtifact; + } + + public List getArtifacts() { + return artifacts; + } + + public Map getManagedVersions() { + return managedVersions; + } + + public List getRemoteRepositories() { + return remoteRepositories; + } + + public boolean isStale() { + File pomFile = pomArtifact.getFile(); + if (pomFile != null) { + if (pomFile.canRead()) { + return length != pomFile.length() || timestamp != pomFile.lastModified(); + } else { + // if the POM didn't exist, retry if any repo is configured to always update + boolean snapshot = pomArtifact.isSnapshot(); + for (ArtifactRepository repository : remoteRepositories) { + ArtifactRepositoryPolicy policy = + snapshot ? repository.getSnapshots() : repository.getReleases(); + if (ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS.equals(policy.getUpdatePolicy())) { + return true; + } + } + } + } + + return length != -1 || timestamp != -1; + } + } + + @Override + public ResolutionGroup get( + Artifact artifact, + boolean resolveManagedVersions, + ArtifactRepository localRepository, + List remoteRepositories) { + CacheKey cacheKey = newCacheKey(artifact, resolveManagedVersions, localRepository, remoteRepositories); + + CacheRecord cacheRecord = cache.get(cacheKey); + + if (cacheRecord != null && !cacheRecord.isStale()) { + Artifact pomArtifact = ArtifactUtils.copyArtifact(cacheRecord.getArtifact()); + Artifact relocatedArtifact = ArtifactUtils.copyArtifactSafe(cacheRecord.getRelocatedArtifact()); + Set artifacts = ArtifactUtils.copyArtifacts(cacheRecord.getArtifacts(), new LinkedHashSet<>()); + Map managedVersions = cacheRecord.getManagedVersions(); + if (managedVersions != null) { + managedVersions = ArtifactUtils.copyArtifacts(managedVersions, new LinkedHashMap<>()); + } + return new ResolutionGroup( + pomArtifact, relocatedArtifact, artifacts, managedVersions, cacheRecord.getRemoteRepositories()); + } + + cache.remove(cacheKey); + + return null; + } + + @Override + public void put( + Artifact artifact, + boolean resolveManagedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ResolutionGroup result) { + put(newCacheKey(artifact, resolveManagedVersions, localRepository, remoteRepositories), result); + } + + protected CacheKey newCacheKey( + Artifact artifact, + boolean resolveManagedVersions, + ArtifactRepository localRepository, + List remoteRepositories) { + return new CacheKey(artifact, resolveManagedVersions, localRepository, remoteRepositories); + } + + protected void put(CacheKey cacheKey, ResolutionGroup result) { + CacheRecord cacheRecord = new CacheRecord( + result.getPomArtifact(), + result.getRelocatedArtifact(), + result.getArtifacts(), + result.getManagedVersions(), + result.getResolutionRepositories()); + + cache.put(cacheKey, cacheRecord); + } + + @Override + public void flush() { + cache.clear(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/DefaultMetadataSource.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/DefaultMetadataSource.java new file mode 100644 index 000000000000..723352f697c5 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/DefaultMetadataSource.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.project.ProjectBuilder; + +/** + * This realizes the metadata source via the default hint to provide backward-compat with Maven 2.x whose Plexus version + * registered component descriptors twice: once keyed by role+roleHint and once keyed by role only. This effectively + * made the metadata source available with its original role hint ("maven") as well as the default hint. + * + */ +@Named +@Singleton +@Deprecated +public class DefaultMetadataSource extends MavenMetadataSource { + @Inject + public DefaultMetadataSource( + RepositoryMetadataManager repositoryMetadataManager, + ArtifactFactory artifactFactory, + ProjectBuilder projectBuilder, + MavenMetadataCache cache, + LegacySupport legacySupport, + MavenRepositorySystem mavenRepositorySystem) { + super(repositoryMetadataManager, artifactFactory, projectBuilder, cache, legacySupport, mavenRepositorySystem); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataCache.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataCache.java new file mode 100644 index 000000000000..bafc22cb8944 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataCache.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * MavenMetadataCache + */ +@Deprecated +public interface MavenMetadataCache { + + ResolutionGroup get( + Artifact artifact, + boolean resolveManagedVersions, + ArtifactRepository localRepository, + List remoteRepositories); + + void put( + Artifact artifact, + boolean resolveManagedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ResolutionGroup result); + + void flush(); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java new file mode 100644 index 000000000000..66d11b5187bc --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java @@ -0,0 +1,715 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException; +import org.apache.maven.artifact.resolver.filter.AndArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ExclusionArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.Relocation; +import org.apache.maven.model.Repository; +import org.apache.maven.model.building.ModelBuildingException; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.resolution.UnresolvableModelException; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.properties.internal.EnvironmentUtils; +import org.apache.maven.properties.internal.SystemProperties; +import org.apache.maven.repository.internal.MavenWorkspaceReader; +import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.RepositoryPolicy; +import org.eclipse.aether.repository.WorkspaceReader; +import org.eclipse.aether.transfer.ArtifactNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + */ +@Named("maven") +@Singleton +@Deprecated +public class MavenMetadataSource implements ArtifactMetadataSource { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final RepositoryMetadataManager repositoryMetadataManager; + private final ArtifactFactory artifactFactory; + private final ProjectBuilder projectBuilder; + private final MavenMetadataCache cache; + private final LegacySupport legacySupport; + + private MavenRepositorySystem mavenRepositorySystem; + + @Inject + public MavenMetadataSource( + RepositoryMetadataManager repositoryMetadataManager, + ArtifactFactory artifactFactory, + ProjectBuilder projectBuilder, + MavenMetadataCache cache, + LegacySupport legacySupport, + MavenRepositorySystem mavenRepositorySystem) { + this.repositoryMetadataManager = repositoryMetadataManager; + this.artifactFactory = artifactFactory; + this.projectBuilder = projectBuilder; + this.cache = cache; + this.legacySupport = legacySupport; + this.mavenRepositorySystem = mavenRepositorySystem; + } + + private void injectSession(MetadataResolutionRequest request) { + RepositorySystemSession session = legacySupport.getRepositorySession(); + + if (session != null) { + request.setOffline(session.isOffline()); + request.setForceUpdate(RepositoryPolicy.UPDATE_POLICY_ALWAYS.equals(session.getUpdatePolicy())); + } + } + + @Override + public ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + return retrieve(artifact, localRepository, remoteRepositories, false); + } + + public ResolutionGroup retrieve( + Artifact artifact, + ArtifactRepository localRepository, + List remoteRepositories, + boolean resolveManagedVersions) + throws ArtifactMetadataRetrievalException { + MetadataResolutionRequest request = new DefaultMetadataResolutionRequest(); + injectSession(request); + request.setArtifact(artifact); + request.setLocalRepository(localRepository); + request.setRemoteRepositories(remoteRepositories); + request.setResolveManagedVersions(resolveManagedVersions); + return retrieve(request); + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException { + Artifact artifact = request.getArtifact(); + + // + // If we have a system scoped artifact, then we do not want any searching in local or remote repositories, + // and we want artifact resolution to only return the system scoped artifact itself. + // + if (artifact.getScope() != null && artifact.getScope().equals(Artifact.SCOPE_SYSTEM)) { + return new ResolutionGroup(null, null, null); + } + + ResolutionGroup cached = cache.get( + artifact, + request.isResolveManagedVersions(), + request.getLocalRepository(), + request.getRemoteRepositories()); + + if (cached != null + // if the POM has no file, we cached a missing artifact, only return the cached data if no update forced + && (!request.isForceUpdate() || hasFile(cached.getPomArtifact()))) { + return cached; + } + + List dependencies; + + List managedDependencies = null; + + List pomRepositories = null; + + Artifact pomArtifact; + + Artifact relocatedArtifact = null; + + // TODO hack: don't rebuild model if it was already loaded during reactor resolution + RepositorySystemSession repositorySession = legacySupport.getRepositorySession(); + final WorkspaceReader workspace = repositorySession.getWorkspaceReader(); + Model model; + if (workspace instanceof MavenWorkspaceReader mavenWorkspaceReader) { + model = mavenWorkspaceReader.findModel(RepositoryUtils.toArtifact(artifact)); + } else { + model = null; + } + + if (model != null) { + pomArtifact = artifact; + dependencies = model.getDependencies(); + DependencyManagement dependencyManagement = model.getDependencyManagement(); + managedDependencies = dependencyManagement == null ? null : dependencyManagement.getDependencies(); + MavenSession session = legacySupport.getSession(); + if (session != null) { + if (session.getProjects() != null) { + pomRepositories = session.getProjects().stream() + .filter(p -> artifact.equals(p.getArtifact())) + .map(MavenProject::getRemoteArtifactRepositories) + .findFirst() + .orElseGet(() -> getRepositoriesFromModel(repositorySession, model)); + } else { + pomRepositories = getRepositoriesFromModel(repositorySession, model); + } + } else { + pomRepositories = new ArrayList<>(); + } + } else if (artifact instanceof ArtifactWithDependencies artifactWithDependencies) { + pomArtifact = artifact; + + dependencies = artifactWithDependencies.getDependencies(); + + managedDependencies = artifactWithDependencies.getManagedDependencies(); + } else { + ProjectRelocation rel = retrieveRelocatedProject(artifact, request); + + if (rel == null) { + return null; + } + + pomArtifact = rel.pomArtifact; + + relocatedArtifact = rel.relocatedArtifact; + + if (rel.project == null) { + // When this happens we have a Maven 1.x POM, or some invalid POM. + // It should have never found its way into Maven 2.x repository, but it did. + dependencies = Collections.emptyList(); + } else { + dependencies = rel.project.getModel().getDependencies(); + + DependencyManagement depMgmt = rel.project.getModel().getDependencyManagement(); + managedDependencies = (depMgmt != null) ? depMgmt.getDependencies() : null; + + pomRepositories = rel.project.getRemoteArtifactRepositories(); + } + } + + Set artifacts = Collections.emptySet(); + + if (!artifact.getArtifactHandler().isIncludesDependencies()) { + artifacts = new LinkedHashSet<>(); + + for (Dependency dependency : dependencies) { + Artifact dependencyArtifact = createDependencyArtifact(dependency, artifact, pomArtifact); + + if (dependencyArtifact != null) { + artifacts.add(dependencyArtifact); + } + } + } + + Map managedVersions = null; + + if (managedDependencies != null && request.isResolveManagedVersions()) { + managedVersions = new HashMap<>(); + + for (Dependency managedDependency : managedDependencies) { + Artifact managedArtifact = createDependencyArtifact(managedDependency, null, pomArtifact); + + managedVersions.put(managedDependency.getManagementKey(), managedArtifact); + } + } + + List aggregatedRepositories = + aggregateRepositories(request.getRemoteRepositories(), pomRepositories); + + ResolutionGroup result = + new ResolutionGroup(pomArtifact, relocatedArtifact, artifacts, managedVersions, aggregatedRepositories); + + cache.put( + artifact, + request.isResolveManagedVersions(), + request.getLocalRepository(), + request.getRemoteRepositories(), + result); + + return result; + } + + private List getRepositoriesFromModel(RepositorySystemSession repositorySession, Model model) { + List pomRepositories = new ArrayList<>(); + for (Repository modelRepository : model.getRepositories()) { + try { + pomRepositories.add(MavenRepositorySystem.buildArtifactRepository(modelRepository)); + } catch (InvalidRepositoryException e) { + // cannot use this then + } + } + mavenRepositorySystem.injectMirror(repositorySession, pomRepositories); + mavenRepositorySystem.injectProxy(repositorySession, pomRepositories); + mavenRepositorySystem.injectAuthentication(repositorySession, pomRepositories); + return pomRepositories; + } + + private boolean hasFile(Artifact artifact) { + return artifact != null + && artifact.getFile() != null + && artifact.getFile().exists(); + } + + private List aggregateRepositories( + List requestRepositories, List pomRepositories) { + List repositories = requestRepositories; + + if (pomRepositories != null && !pomRepositories.isEmpty()) { + Map repos = new LinkedHashMap<>(); + + for (ArtifactRepository repo : requestRepositories) { + if (!repos.containsKey(repo.getId())) { + repos.put(repo.getId(), repo); + } + } + + for (ArtifactRepository repo : pomRepositories) { + if (!repos.containsKey(repo.getId())) { + repos.put(repo.getId(), repo); + } + } + + repositories = new ArrayList<>(repos.values()); + } + + return repositories; + } + + private Artifact createDependencyArtifact(Dependency dependency, Artifact owner, Artifact pom) + throws ArtifactMetadataRetrievalException { + try { + String inheritedScope = (owner != null) ? owner.getScope() : null; + + ArtifactFilter inheritedFilter = (owner != null) ? owner.getDependencyFilter() : null; + + return createDependencyArtifact(artifactFactory, dependency, inheritedScope, inheritedFilter); + } catch (InvalidVersionSpecificationException e) { + throw new ArtifactMetadataRetrievalException( + "Invalid version for dependency " + dependency.getManagementKey() + ": " + e.getMessage(), e, pom); + } + } + + private static Artifact createDependencyArtifact( + ArtifactFactory factory, Dependency dependency, String inheritedScope, ArtifactFilter inheritedFilter) + throws InvalidVersionSpecificationException { + String effectiveScope = getEffectiveScope(dependency.getScope(), inheritedScope); + + if (effectiveScope == null) { + return null; + } + + VersionRange versionRange = VersionRange.createFromVersionSpec(dependency.getVersion()); + + Artifact dependencyArtifact = factory.createDependencyArtifact( + dependency.getGroupId(), + dependency.getArtifactId(), + versionRange, + dependency.getType(), + dependency.getClassifier(), + effectiveScope, + dependency.isOptional()); + + if (inheritedFilter != null && !inheritedFilter.include(dependencyArtifact)) { + return null; + } + + if (Artifact.SCOPE_SYSTEM.equals(effectiveScope)) { + dependencyArtifact.setFile(new File(dependency.getSystemPath())); + } + + dependencyArtifact.setDependencyFilter(createDependencyFilter(dependency, inheritedFilter)); + + return dependencyArtifact; + } + + private static String getEffectiveScope(String originalScope, String inheritedScope) { + String effectiveScope = Artifact.SCOPE_RUNTIME; + + if (originalScope == null) { + originalScope = Artifact.SCOPE_COMPILE; + } + + if (inheritedScope == null) { + // direct dependency retains its scope + effectiveScope = originalScope; + } else if (Artifact.SCOPE_TEST.equals(originalScope) || Artifact.SCOPE_PROVIDED.equals(originalScope)) { + // test and provided are not transitive, so exclude them + effectiveScope = null; + } else if (Artifact.SCOPE_SYSTEM.equals(originalScope)) { + // system scope come through unchanged... + effectiveScope = Artifact.SCOPE_SYSTEM; + } else if (Artifact.SCOPE_COMPILE.equals(originalScope) && Artifact.SCOPE_COMPILE.equals(inheritedScope)) { + // added to retain compile scope. Remove if you want compile inherited as runtime + effectiveScope = Artifact.SCOPE_COMPILE; + } else if (Artifact.SCOPE_TEST.equals(inheritedScope)) { + effectiveScope = Artifact.SCOPE_TEST; + } else if (Artifact.SCOPE_PROVIDED.equals(inheritedScope)) { + effectiveScope = Artifact.SCOPE_PROVIDED; + } + + return effectiveScope; + } + + private static ArtifactFilter createDependencyFilter(Dependency dependency, ArtifactFilter inheritedFilter) { + ArtifactFilter effectiveFilter = inheritedFilter; + + if (!dependency.getExclusions().isEmpty()) { + effectiveFilter = new ExclusionArtifactFilter(dependency.getExclusions()); + + if (inheritedFilter != null) { + effectiveFilter = new AndArtifactFilter(Arrays.asList(inheritedFilter, effectiveFilter)); + } + } + + return effectiveFilter; + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + MetadataResolutionRequest request = new DefaultMetadataResolutionRequest(); + injectSession(request); + request.setArtifact(artifact); + request.setLocalRepository(localRepository); + request.setRemoteRepositories(remoteRepositories); + return retrieveAvailableVersions(request); + } + + @Override + public List retrieveAvailableVersions(MetadataResolutionRequest request) + throws ArtifactMetadataRetrievalException { + RepositoryMetadata metadata = new ArtifactRepositoryMetadata(request.getArtifact()); + + try { + repositoryMetadataManager.resolve(metadata, request); + } catch (RepositoryMetadataResolutionException e) { + throw new ArtifactMetadataRetrievalException(e.getMessage(), e, request.getArtifact()); + } + + List availableVersions = request.getLocalRepository().findVersions(request.getArtifact()); + + return retrieveAvailableVersionsFromMetadata(metadata.getMetadata(), availableVersions); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository deploymentRepository) + throws ArtifactMetadataRetrievalException { + RepositoryMetadata metadata = new ArtifactRepositoryMetadata(artifact); + + try { + repositoryMetadataManager.resolveAlways(metadata, localRepository, deploymentRepository); + } catch (RepositoryMetadataResolutionException e) { + throw new ArtifactMetadataRetrievalException(e.getMessage(), e, artifact); + } + + List availableVersions = localRepository.findVersions(artifact); + + return retrieveAvailableVersionsFromMetadata(metadata.getMetadata(), availableVersions); + } + + private List retrieveAvailableVersionsFromMetadata( + Metadata repoMetadata, List availableVersions) { + Collection versions = new LinkedHashSet<>(); + + if ((repoMetadata != null) && (repoMetadata.getVersioning() != null)) { + versions.addAll(repoMetadata.getVersioning().getVersions()); + } + + versions.addAll(availableVersions); + + List artifactVersions = new ArrayList<>(versions.size()); + + for (String version : versions) { + artifactVersions.add(new DefaultArtifactVersion(version)); + } + + return artifactVersions; + } + + // USED BY MAVEN ASSEMBLY PLUGIN + @Deprecated + public static Set createArtifacts( + ArtifactFactory artifactFactory, + List dependencies, + String inheritedScope, + ArtifactFilter dependencyFilter, + MavenProject project) + throws InvalidDependencyVersionException { + Set artifacts = new LinkedHashSet<>(); + + for (Dependency d : dependencies) { + Artifact dependencyArtifact; + try { + dependencyArtifact = createDependencyArtifact(artifactFactory, d, inheritedScope, dependencyFilter); + } catch (InvalidVersionSpecificationException e) { + throw new InvalidDependencyVersionException(project.getId(), d, project.getFile(), e); + } + + if (dependencyArtifact != null) { + artifacts.add(dependencyArtifact); + } + } + + return artifacts; + } + + @SuppressWarnings("checkstyle:methodlength") + private ProjectRelocation retrieveRelocatedProject(Artifact artifact, MetadataResolutionRequest repositoryRequest) + throws ArtifactMetadataRetrievalException { + MavenProject project; + + Artifact pomArtifact; + Artifact relocatedArtifact = null; + boolean done = false; + do { + project = null; + + pomArtifact = artifactFactory.createProjectArtifact( + artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getScope()); + + if ("pom".equals(artifact.getType())) { + pomArtifact.setFile(artifact.getFile()); + } + + if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) { + done = true; + } else { + try { + ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); + configuration.setLocalRepository(repositoryRequest.getLocalRepository()); + configuration.setRemoteRepositories(repositoryRequest.getRemoteRepositories()); + configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + configuration.setProcessPlugins(false); + configuration.setRepositoryMerging(ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT); + MavenSession session = legacySupport.getSession(); + if (session != null) { + configuration.setSystemProperties(session.getSystemProperties()); + configuration.setUserProperties(session.getUserProperties()); + } else { + configuration.setSystemProperties(getSystemProperties()); + configuration.setUserProperties(new Properties()); + } + configuration.setRepositorySession(legacySupport.getRepositorySession()); + + project = projectBuilder.build(pomArtifact, configuration).getProject(); + } catch (ProjectBuildingException e) { + ModelProblem missingParentPom = hasMissingParentPom(e); + if (missingParentPom != null) { + throw new ArtifactMetadataRetrievalException( + "Failed to process POM for " + artifact.getId() + ": " + missingParentPom.getMessage(), + missingParentPom.getException(), + artifact); + } + + String message; + + if (isMissingPom(e)) { + message = "Missing POM for " + artifact.getId(); + } else if (isNonTransferablePom(e)) { + throw new ArtifactMetadataRetrievalException( + "Failed to retrieve POM for " + artifact.getId() + ": " + + e.getCause().getMessage(), + e.getCause(), + artifact); + } else { + message = "Invalid POM for " + artifact.getId() + + ", transitive dependencies (if any) will not be available" + + ", enable verbose output (-X) for more details"; + } + + if (logger.isDebugEnabled()) { + message += ": " + e.getMessage(); + } + + logger.warn(message); + } + + if (project != null) { + Relocation relocation = null; + + DistributionManagement distMgmt = project.getModel().getDistributionManagement(); + if (distMgmt != null) { + relocation = distMgmt.getRelocation(); + + artifact.setDownloadUrl(distMgmt.getDownloadUrl()); + pomArtifact.setDownloadUrl(distMgmt.getDownloadUrl()); + } + + if (relocation != null) { + if (relocation.getGroupId() != null) { + artifact.setGroupId(relocation.getGroupId()); + relocatedArtifact = artifact; + project.setGroupId(relocation.getGroupId()); + } + if (relocation.getArtifactId() != null) { + artifact.setArtifactId(relocation.getArtifactId()); + relocatedArtifact = artifact; + project.setArtifactId(relocation.getArtifactId()); + } + if (relocation.getVersion() != null) { + // note: see MNG-3454. This causes a problem, but fixing it may break more. + artifact.setVersionRange(VersionRange.createFromVersion(relocation.getVersion())); + relocatedArtifact = artifact; + project.setVersion(relocation.getVersion()); + } + + if (artifact.getDependencyFilter() != null + && !artifact.getDependencyFilter().include(artifact)) { + return null; + } + + // MNG-2861: the artifact data has changed. If the available versions where previously + // retrieved, we need to update it. + // TODO shouldn't the versions be merged across relocations? + List available = artifact.getAvailableVersions(); + if (available != null && !available.isEmpty()) { + MetadataResolutionRequest metadataRequest = + new DefaultMetadataResolutionRequest(repositoryRequest); + metadataRequest.setArtifact(artifact); + available = retrieveAvailableVersions(metadataRequest); + artifact.setAvailableVersions(available); + } + + String message = " this artifact has been relocated to " + artifact.getGroupId() + ":" + + artifact.getArtifactId() + ":" + artifact.getVersion() + "."; + + if (relocation.getMessage() != null) { + message += " " + relocation.getMessage(); + } + + if (artifact.getDependencyTrail() != null + && artifact.getDependencyTrail().size() == 1) { + logger.warn( + "While downloading {}:{}:{}{}", + pomArtifact.getGroupId(), + pomArtifact.getArtifactId(), + pomArtifact.getVersion(), + message); + } else { + logger.debug( + "While downloading {}:{}:{}{}", + pomArtifact.getGroupId(), + pomArtifact.getArtifactId(), + pomArtifact.getVersion(), + message); + } + } else { + done = true; + } + } else { + done = true; + } + } + } while (!done); + + ProjectRelocation rel = new ProjectRelocation(); + rel.project = project; + rel.pomArtifact = pomArtifact; + rel.relocatedArtifact = relocatedArtifact; + + return rel; + } + + private ModelProblem hasMissingParentPom(ProjectBuildingException e) { + if (e.getCause() instanceof ModelBuildingException mbe) { + for (ModelProblem problem : mbe.getProblems()) { + if (problem.getException() instanceof UnresolvableModelException) { + return problem; + } + } + } + return null; + } + + private boolean isMissingPom(Exception e) { + if (e.getCause() instanceof MultipleArtifactsNotFoundException) { + return true; + } + return e.getCause() instanceof org.eclipse.aether.resolution.ArtifactResolutionException + && e.getCause().getCause() instanceof ArtifactNotFoundException; + } + + private boolean isNonTransferablePom(Exception e) { + if (e.getCause() instanceof ArtifactResolutionException) { + return true; + } + return e.getCause() instanceof org.eclipse.aether.resolution.ArtifactResolutionException + && !(e.getCause().getCause() instanceof ArtifactNotFoundException); + } + + private Properties getSystemProperties() { + Properties props = new Properties(); + + EnvironmentUtils.addEnvVars(props); + + SystemProperties.addSystemProperties(props); + + return props; + } + + private static final class ProjectRelocation { + private MavenProject project; + + private Artifact pomArtifact; + + private Artifact relocatedArtifact; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/ProjectArtifactMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/ProjectArtifactMetadata.java new file mode 100644 index 000000000000..3c21e4216636 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/ProjectArtifactMetadata.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.AbstractArtifactMetadata; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataStoreException; + +/** + * Attach a POM to an artifact. + * + */ +@Deprecated +public class ProjectArtifactMetadata extends AbstractArtifactMetadata { + private final File file; + + public ProjectArtifactMetadata(Artifact artifact) { + this(artifact, null); + } + + public ProjectArtifactMetadata(Artifact artifact, File file) { + super(artifact); + this.file = file; + } + + public File getFile() { + return file; + } + + @Override + public String getRemoteFilename() { + return getFilename(); + } + + @Override + public String getLocalFilename(ArtifactRepository repository) { + return getFilename(); + } + + private String getFilename() { + return getArtifactId() + "-" + artifact.getVersion() + ".pom"; + } + + @Override + public void storeInLocalRepository(ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataStoreException { + File destination = new File( + localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata(this, remoteRepository)); + + // ---------------------------------------------------------------------------- + // I'm fully aware that the file could just be moved using File.rename but + // there are bugs in various JVM that have problems doing this across + // different filesystem. So we'll incur the small hit to actually copy + // here and be safe. jvz. + // ---------------------------------------------------------------------------- + + try { + Files.createDirectories(destination.toPath().getParent()); + Files.copy(file.toPath(), destination.toPath()); + } catch (IOException e) { + throw new RepositoryMetadataStoreException("Error copying POM to the local repository.", e); + } + } + + @Override + public String toString() { + return "project information for " + artifact.getArtifactId() + " " + artifact.getVersion(); + } + + @Override + public boolean storedInArtifactVersionDirectory() { + return true; + } + + @Override + public String getBaseVersion() { + return artifact.getBaseVersion(); + } + + @Override + public Object getKey() { + return "project " + artifact.getGroupId() + ":" + artifact.getArtifactId(); + } + + @Override + public void merge(ArtifactMetadata metadata) { + ProjectArtifactMetadata m = (ProjectArtifactMetadata) metadata; + if (!m.file.equals(file)) { + throw new IllegalStateException("Cannot add two different pieces of metadata for: " + getKey()); + } + } + + @Override + public void merge(org.apache.maven.repository.legacy.metadata.ArtifactMetadata metadata) { + this.merge((ArtifactMetadata) metadata); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/inheritance/DefaultModelInheritanceAssembler.java b/compat/maven-compat/src/main/java/org/apache/maven/project/inheritance/DefaultModelInheritanceAssembler.java new file mode 100644 index 000000000000..c265dc0042ed --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/inheritance/DefaultModelInheritanceAssembler.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.model.Build; +import org.apache.maven.model.Model; + +/** + * DefaultModelInheritanceAssembler + */ +@Named +@Singleton +@Deprecated +public class DefaultModelInheritanceAssembler implements ModelInheritanceAssembler { + @Override + public void assembleModelInheritance(Model child, Model parent, String childPathAdjustment) { + throw new UnsupportedOperationException(); + } + + @Override + public void assembleModelInheritance(Model child, Model parent) { + throw new UnsupportedOperationException(); + } + + @Override + public void assembleBuildInheritance(Build childBuild, Build parentBuild, boolean handleAsInheritance) { + throw new UnsupportedOperationException(); + } + + @Override + public void copyModel(Model dest, Model source) { + throw new UnsupportedOperationException(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/inheritance/ModelInheritanceAssembler.java b/compat/maven-compat/src/main/java/org/apache/maven/project/inheritance/ModelInheritanceAssembler.java new file mode 100644 index 000000000000..df0fc5ab4288 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/inheritance/ModelInheritanceAssembler.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance; + +import org.apache.maven.model.Build; +import org.apache.maven.model.Model; + +/** + */ +@Deprecated +public interface ModelInheritanceAssembler { + String ROLE = ModelInheritanceAssembler.class.getName(); + + void assembleModelInheritance(Model child, Model parent, String childPathAdjustment); + + void assembleModelInheritance(Model child, Model parent); + + void assembleBuildInheritance(Build childBuild, Build parentBuild, boolean handleAsInheritance); + + void copyModel(Model dest, Model source); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/AbstractStringBasedModelInterpolator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/AbstractStringBasedModelInterpolator.java new file mode 100644 index 000000000000..5fa63fc61099 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/AbstractStringBasedModelInterpolator.java @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +import javax.inject.Inject; +import javax.xml.stream.XMLStreamException; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.model.Model; +import org.apache.maven.model.v4.MavenStaxReader; +import org.apache.maven.model.v4.MavenStaxWriter; +import org.apache.maven.project.DefaultProjectBuilderConfiguration; +import org.apache.maven.project.ProjectBuilderConfiguration; +import org.apache.maven.project.path.PathTranslator; +import org.codehaus.plexus.interpolation.AbstractValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.InterpolationPostProcessor; +import org.codehaus.plexus.interpolation.Interpolator; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper; +import org.codehaus.plexus.interpolation.RecursionInterceptor; +import org.codehaus.plexus.interpolation.ValueSource; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; + +/** + * Use a regular expression search to find and resolve expressions within the POM. + * + * TODO Consolidate this logic with the PluginParameterExpressionEvaluator, minus deprecations/bans. + */ +@Deprecated +public abstract class AbstractStringBasedModelInterpolator extends AbstractLogEnabled + implements ModelInterpolator, Initializable { + + private static final List PROJECT_PREFIXES = Arrays.asList("pom.", "project."); + + private static final List TRANSLATED_PATH_EXPRESSIONS; + + static { + List translatedPrefixes = new ArrayList<>(); + + // MNG-1927, MNG-2124, MNG-3355: + // If the build section is present and the project directory is non-null, we should make + // sure interpolation of the directories below uses translated paths. + // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the + // code below... + translatedPrefixes.add("build.directory"); + translatedPrefixes.add("build.outputDirectory"); + translatedPrefixes.add("build.testOutputDirectory"); + translatedPrefixes.add("build.sourceDirectory"); + translatedPrefixes.add("build.testSourceDirectory"); + translatedPrefixes.add("build.scriptSourceDirectory"); + translatedPrefixes.add("reporting.outputDirectory"); + + TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes; + } + + @Inject + private PathTranslator pathTranslator; + + private Interpolator interpolator; + + private RecursionInterceptor recursionInterceptor; + + // for testing. + protected AbstractStringBasedModelInterpolator(PathTranslator pathTranslator) { + this.pathTranslator = pathTranslator; + } + + protected AbstractStringBasedModelInterpolator() {} + + @Override + public Model interpolate(Model model, Map context) throws ModelInterpolationException { + return interpolate(model, context, true); + } + + /** + * Serialize the inbound Model instance to a StringWriter, perform the regex replacement to resolve + * POM expressions, then re-parse into the resolved Model instance. + *

    + * NOTE: This will result in a different instance of Model being returned!!! + * + * @param model The inbound Model instance, to serialize and reference for expression resolution + * @param context The other context map to be used during resolution + * + * @return The resolved instance of the inbound Model. This is a different instance! + * + * @deprecated Use {@link ModelInterpolator#interpolate(Model, File, ProjectBuilderConfiguration, boolean)} instead. + */ + @Deprecated + @Override + public Model interpolate(Model model, Map context, boolean strict) throws ModelInterpolationException { + Properties props = new Properties(); + props.putAll(context); + + return interpolate(model, null, new DefaultProjectBuilderConfiguration().setExecutionProperties(props), true); + } + + @Override + public Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) + throws ModelInterpolationException { + StringWriter sWriter = new StringWriter(1024); + + MavenStaxWriter writer = new MavenStaxWriter(); + try { + writer.write(sWriter, model.getDelegate()); + } catch (IOException | XMLStreamException e) { + throw new ModelInterpolationException("Cannot serialize project model for interpolation.", e); + } + + String serializedModel = sWriter.toString(); + serializedModel = interpolate(serializedModel, model, projectDir, config, debugEnabled); + + StringReader sReader = new StringReader(serializedModel); + + MavenStaxReader modelReader = new MavenStaxReader(); + try { + model = new Model(modelReader.read(sReader)); + } catch (XMLStreamException e) { + throw new ModelInterpolationException( + "Cannot read project model from interpolating filter of serialized version.", e); + } + + return model; + } + + /** + * Interpolates all expressions in the src parameter. + *

    + * The algorithm used for each expression is: + *

      + *
    • If it starts with either "pom." or "project.", the expression is evaluated against the model.
    • + *
    • If the value is null, get the value from the context.
    • + *
    • If the value is null, but the context contains the expression, don't replace the expression string + * with the value, and continue to find other expressions.
    • + *
    • If the value is null, get it from the model properties.
    • + *
    + */ + @Override + public String interpolate( + String src, Model model, final File projectDir, ProjectBuilderConfiguration config, boolean debug) + throws ModelInterpolationException { + try { + List valueSources = createValueSources(model, projectDir, config); + List postProcessors = createPostProcessors(model, projectDir, config); + + return interpolateInternal(src, valueSources, postProcessors, debug); + } finally { + interpolator.clearAnswers(); + } + } + + protected List createValueSources( + final Model model, final File projectDir, final ProjectBuilderConfiguration config) { + String timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT; + + Properties modelProperties = model.getProperties(); + if (modelProperties != null) { + timestampFormat = modelProperties.getProperty(BUILD_TIMESTAMP_FORMAT_PROPERTY, timestampFormat); + } + + ValueSource modelValueSource1 = new PrefixedObjectValueSource(PROJECT_PREFIXES, model, false); + ValueSource modelValueSource2 = new ObjectBasedValueSource(model); + + ValueSource basedirValueSource = new PrefixedValueSourceWrapper( + new AbstractValueSource(false) { + + @Override + public Object getValue(String expression) { + if (projectDir != null && "basedir".equals(expression)) { + return projectDir.getAbsolutePath(); + } + return null; + } + }, + PROJECT_PREFIXES, + true); + ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( + new AbstractValueSource(false) { + + @Override + public Object getValue(String expression) { + if (projectDir != null && "baseUri".equals(expression)) { + return projectDir.getAbsoluteFile().toPath().toUri().toASCIIString(); + } + return null; + } + }, + PROJECT_PREFIXES, + false); + + List valueSources = new ArrayList<>(9); + + // NOTE: Order counts here! + valueSources.add(basedirValueSource); + valueSources.add(baseUriValueSource); + valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), timestampFormat)); + valueSources.add(modelValueSource1); + valueSources.add(new MapBasedValueSource(config.getUserProperties())); + valueSources.add(new MapBasedValueSource(modelProperties)); + valueSources.add(new MapBasedValueSource(config.getExecutionProperties())); + valueSources.add(new AbstractValueSource(false) { + + @Override + public Object getValue(String expression) { + return config.getExecutionProperties().getProperty("env." + expression); + } + }); + valueSources.add(modelValueSource2); + + return valueSources; + } + + protected List createPostProcessors( + final Model model, final File projectDir, final ProjectBuilderConfiguration config) { + return Collections.singletonList(new PathTranslatingPostProcessor( + PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator)); + } + + @SuppressWarnings("unchecked") + protected String interpolateInternal( + String src, List valueSources, List postProcessors, boolean debug) + throws ModelInterpolationException { + if (!src.contains("${")) { + return src; + } + + Logger logger = getLogger(); + + String result = src; + synchronized (this) { + for (ValueSource vs : valueSources) { + interpolator.addValueSource(vs); + } + + for (InterpolationPostProcessor postProcessor : postProcessors) { + interpolator.addPostProcessor(postProcessor); + } + + try { + try { + result = interpolator.interpolate(result, recursionInterceptor); + } catch (InterpolationException e) { + throw new ModelInterpolationException(e.getMessage(), e); + } + + if (debug) { + List feedback = interpolator.getFeedback(); + if (feedback != null && !feedback.isEmpty()) { + logger.debug("Maven encountered the following problems during initial POM interpolation:"); + + Object last = null; + for (Object next : feedback) { + if (next instanceof Throwable throwable) { + if (last == null) { + logger.debug("", throwable); + } else { + logger.debug(String.valueOf(last), throwable); + } + } else { + if (last != null) { + logger.debug(String.valueOf(last)); + } + + last = next; + } + } + + if (last != null) { + logger.debug(String.valueOf(last)); + } + } + } + + interpolator.clearFeedback(); + } finally { + for (ValueSource vs : valueSources) { + interpolator.removeValuesSource(vs); + } + + for (InterpolationPostProcessor postProcessor : postProcessors) { + interpolator.removePostProcessor(postProcessor); + } + } + } + + return result; + } + + protected RecursionInterceptor getRecursionInterceptor() { + return recursionInterceptor; + } + + protected void setRecursionInterceptor(RecursionInterceptor recursionInterceptor) { + this.recursionInterceptor = recursionInterceptor; + } + + protected abstract Interpolator createInterpolator(); + + @Override + public void initialize() throws InitializationException { + interpolator = createInterpolator(); + recursionInterceptor = new PrefixAwareRecursionInterceptor(PROJECT_PREFIXES); + } + + protected final Interpolator getInterpolator() { + return interpolator; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/BuildTimestampValueSource.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/BuildTimestampValueSource.java new file mode 100644 index 000000000000..263f04ee957c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/BuildTimestampValueSource.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.codehaus.plexus.interpolation.AbstractValueSource; + +/** + * + */ +@Deprecated +public class BuildTimestampValueSource extends AbstractValueSource { + + private final Date startTime; + + private final String format; + + private String formattedDate; + + public BuildTimestampValueSource(Date startTime, String format) { + super(false); + this.startTime = startTime; + this.format = format; + } + + @Override + public Object getValue(String expression) { + if ("build.timestamp".equals(expression) || "maven.build.timestamp".equals(expression)) { + if (formattedDate == null && startTime != null) { + formattedDate = new SimpleDateFormat(format).format(startTime); + } + + return formattedDate; + } + + return null; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/ModelInterpolationException.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/ModelInterpolationException.java new file mode 100644 index 000000000000..9200a094bf88 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/ModelInterpolationException.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +/** + */ +@SuppressWarnings("serial") +@Deprecated +public class ModelInterpolationException extends Exception { + private String expression; + + private String originalMessage; + + public ModelInterpolationException(String message) { + super(message); + } + + public ModelInterpolationException(String message, Throwable cause) { + super(message, cause); + } + + public ModelInterpolationException(String expression, String message, Throwable cause) { + super("The POM expression: " + expression + " could not be evaluated. Reason: " + message, cause); + + this.expression = expression; + this.originalMessage = message; + } + + public ModelInterpolationException(String expression, String message) { + super("The POM expression: " + expression + " could not be evaluated. Reason: " + message); + + this.expression = expression; + this.originalMessage = message; + } + + public String getExpression() { + return expression; + } + + public String getOriginalMessage() { + return originalMessage; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/ModelInterpolator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/ModelInterpolator.java new file mode 100644 index 000000000000..42d4f1240741 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/ModelInterpolator.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +import java.io.File; +import java.util.Map; + +import org.apache.maven.model.Model; +import org.apache.maven.project.ProjectBuilderConfiguration; + +/** + */ +@Deprecated +public interface ModelInterpolator { + String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyyMMdd-HHmm"; + + String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format"; + + String ROLE = ModelInterpolator.class.getName(); + + /** + * @deprecated Use {@link ModelInterpolator#interpolate(Model, File, ProjectBuilderConfiguration, boolean)} instead. + */ + @Deprecated + Model interpolate(Model project, Map context) throws ModelInterpolationException; + + /** + * @deprecated Use {@link ModelInterpolator#interpolate(Model, File, ProjectBuilderConfiguration, boolean)} instead. + */ + @Deprecated + Model interpolate(Model model, Map context, boolean strict) throws ModelInterpolationException; + + Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) + throws ModelInterpolationException; + + String interpolate( + String src, Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) + throws ModelInterpolationException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/PathTranslatingPostProcessor.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/PathTranslatingPostProcessor.java new file mode 100644 index 000000000000..255826d3ea87 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/PathTranslatingPostProcessor.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +import java.io.File; +import java.util.List; + +import org.apache.maven.project.path.PathTranslator; +import org.codehaus.plexus.interpolation.InterpolationPostProcessor; +import org.codehaus.plexus.interpolation.util.ValueSourceUtils; + +/** + * + */ +@Deprecated +public class PathTranslatingPostProcessor implements InterpolationPostProcessor { + + private final List unprefixedPathKeys; + private final File projectDir; + private final PathTranslator pathTranslator; + private final List expressionPrefixes; + + public PathTranslatingPostProcessor( + List expressionPrefixes, + List unprefixedPathKeys, + File projectDir, + PathTranslator pathTranslator) { + this.expressionPrefixes = expressionPrefixes; + this.unprefixedPathKeys = unprefixedPathKeys; + this.projectDir = projectDir; + this.pathTranslator = pathTranslator; + } + + @Override + public Object execute(String expression, Object value) { + expression = ValueSourceUtils.trimPrefix(expression, expressionPrefixes, true); + + if (projectDir != null && value != null && unprefixedPathKeys.contains(expression)) { + return pathTranslator.alignToBaseDirectory(String.valueOf(value), projectDir); + } + + return value; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/RegexBasedModelInterpolator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/RegexBasedModelInterpolator.java new file mode 100644 index 000000000000..6449da7416ce --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/RegexBasedModelInterpolator.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +import java.io.IOException; +import java.util.Properties; + +import org.apache.maven.project.path.PathTranslator; +import org.codehaus.plexus.interpolation.Interpolator; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; + +/** + * Use a regular expression search to find and resolve expressions within the POM. + * + * TODO Consolidate this logic with the PluginParameterExpressionEvaluator, minus deprecations/bans. + */ +@Deprecated +public class RegexBasedModelInterpolator extends AbstractStringBasedModelInterpolator { + + public RegexBasedModelInterpolator() throws IOException {} + + public RegexBasedModelInterpolator(PathTranslator pathTranslator) { + super(pathTranslator); + } + + public RegexBasedModelInterpolator(Properties envars) {} + + @Override + protected Interpolator createInterpolator() { + return new RegexBasedInterpolator(true); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java new file mode 100644 index 000000000000..c9d941a446e1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.interpolation; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.maven.model.Model; +import org.apache.maven.project.ProjectBuilderConfiguration; +import org.apache.maven.project.path.PathTranslator; +import org.codehaus.plexus.interpolation.InterpolationPostProcessor; +import org.codehaus.plexus.interpolation.Interpolator; +import org.codehaus.plexus.interpolation.StringSearchInterpolator; +import org.codehaus.plexus.interpolation.ValueSource; +import org.codehaus.plexus.logging.Logger; + +/** + * StringSearchModelInterpolator + */ +@Deprecated +@Named +@Singleton +public class StringSearchModelInterpolator extends AbstractStringBasedModelInterpolator { + + private static final Map, Field[]> FIELDS_BY_CLASS = new WeakHashMap<>(); + private static final Map, Boolean> PRIMITIVE_BY_CLASS = new WeakHashMap<>(); + + public StringSearchModelInterpolator() {} + + public StringSearchModelInterpolator(PathTranslator pathTranslator) { + super(pathTranslator); + } + + @Override + public Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) + throws ModelInterpolationException { + interpolateObject(model, model, projectDir, config, debugEnabled); + + return model; + } + + protected void interpolateObject( + Object obj, Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) + throws ModelInterpolationException { + try { + List valueSources = createValueSources(model, projectDir, config); + List postProcessors = createPostProcessors(model, projectDir, config); + + InterpolateObjectAction action = + new InterpolateObjectAction(obj, valueSources, postProcessors, debugEnabled, this, getLogger()); + + ModelInterpolationException error = AccessController.doPrivileged(action); + + if (error != null) { + throw error; + } + } finally { + getInterpolator().clearAnswers(); + } + } + + @Override + protected Interpolator createInterpolator() { + StringSearchInterpolator interpolator = new StringSearchInterpolator(); + interpolator.setCacheAnswers(true); + + return interpolator; + } + + private static final class InterpolateObjectAction implements PrivilegedAction { + + private final boolean debugEnabled; + private final LinkedList interpolationTargets; + private final StringSearchModelInterpolator modelInterpolator; + private final Logger logger; + private final List valueSources; + private final List postProcessors; + + InterpolateObjectAction( + Object target, + List valueSources, + List postProcessors, + boolean debugEnabled, + StringSearchModelInterpolator modelInterpolator, + Logger logger) { + this.valueSources = valueSources; + this.postProcessors = postProcessors; + this.debugEnabled = debugEnabled; + + this.interpolationTargets = new LinkedList<>(); + interpolationTargets.add(target); + + this.modelInterpolator = modelInterpolator; + this.logger = logger; + } + + @Override + public ModelInterpolationException run() { + while (!interpolationTargets.isEmpty()) { + Object obj = interpolationTargets.removeFirst(); + + try { + traverseObjectWithParents(obj.getClass(), obj); + } catch (ModelInterpolationException e) { + return e; + } + } + + return null; + } + + @SuppressWarnings({"unchecked", "checkstyle:methodlength"}) + private void traverseObjectWithParents(Class cls, Object target) throws ModelInterpolationException { + if (cls == null) { + return; + } + + if (cls.isArray()) { + evaluateArray(target); + } else if (isQualifiedForInterpolation(cls)) { + Field[] fields = FIELDS_BY_CLASS.computeIfAbsent(cls, k -> cls.getDeclaredFields()); + + for (Field field : fields) { + Class type = field.getType(); + if (isQualifiedForInterpolation(field, type)) { + boolean isAccessible = field.isAccessible(); + field.setAccessible(true); + try { + try { + if (String.class == type) { + String value = (String) field.get(target); + if (value != null) { + String interpolated = modelInterpolator.interpolateInternal( + value, valueSources, postProcessors, debugEnabled); + + if (!interpolated.equals(value)) { + field.set(target, interpolated); + } + } + } else if (Collection.class.isAssignableFrom(type)) { + Collection c = (Collection) field.get(target); + if (c != null && !c.isEmpty()) { + List originalValues = new ArrayList<>(c); + try { + c.clear(); + } catch (UnsupportedOperationException e) { + if (debugEnabled && logger != null) { + logger.debug("Skipping interpolation of field: " + field + " in: " + + cls.getName() + + "; it is an unmodifiable collection."); + } + continue; + } + + for (Object value : originalValues) { + if (value != null) { + if (String.class == value.getClass()) { + String interpolated = modelInterpolator.interpolateInternal( + (String) value, valueSources, postProcessors, debugEnabled); + + if (!interpolated.equals(value)) { + c.add(interpolated); + } else { + c.add(value); + } + } else { + c.add(value); + if (value.getClass().isArray()) { + evaluateArray(value); + } else { + interpolationTargets.add(value); + } + } + } else { + // add the null back in...not sure what else to do... + c.add(value); + } + } + } + } else if (Map.class.isAssignableFrom(type)) { + Map m = (Map) field.get(target); + if (m != null && !m.isEmpty()) { + for (Map.Entry entry : m.entrySet()) { + Object value = entry.getValue(); + + if (value != null) { + if (String.class == value.getClass()) { + String interpolated = modelInterpolator.interpolateInternal( + (String) value, valueSources, postProcessors, debugEnabled); + + if (!interpolated.equals(value)) { + try { + entry.setValue(interpolated); + } catch (UnsupportedOperationException e) { + if (debugEnabled && logger != null) { + logger.debug("Skipping interpolation of field: " + field + + " (key: " + entry.getKey() + ") in: " + + cls.getName() + + "; it is an unmodifiable collection."); + } + } + } + } else { + if (value.getClass().isArray()) { + evaluateArray(value); + } else { + interpolationTargets.add(value); + } + } + } + } + } + } else { + Object value = field.get(target); + if (value != null) { + if (field.getType().isArray()) { + evaluateArray(value); + } else { + interpolationTargets.add(value); + } + } + } + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new ModelInterpolationException( + "Failed to interpolate field: " + field + " on class: " + cls.getName(), e); + } + } finally { + field.setAccessible(isAccessible); + } + } + } + + traverseObjectWithParents(cls.getSuperclass(), target); + } + } + + private boolean isQualifiedForInterpolation(Class cls) { + return !cls.getPackage().getName().startsWith("java") + && !cls.getPackage().getName().startsWith("sun.nio.fs"); + } + + private boolean isQualifiedForInterpolation(Field field, Class fieldType) { + if (!PRIMITIVE_BY_CLASS.containsKey(fieldType)) { + PRIMITIVE_BY_CLASS.put(fieldType, fieldType.isPrimitive()); + } + + if (PRIMITIVE_BY_CLASS.get(fieldType)) { + return false; + } + + return !"parent".equals(field.getName()); + } + + private void evaluateArray(Object target) throws ModelInterpolationException { + int len = Array.getLength(target); + for (int i = 0; i < len; i++) { + Object value = Array.get(target, i); + if (value != null) { + if (String.class == value.getClass()) { + String interpolated = modelInterpolator.interpolateInternal( + (String) value, valueSources, postProcessors, debugEnabled); + + if (!interpolated.equals(value)) { + Array.set(target, i, interpolated); + } + } else { + interpolationTargets.add(value); + } + } + } + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/path/DefaultPathTranslator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/path/DefaultPathTranslator.java new file mode 100644 index 000000000000..8567e9765cc2 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/path/DefaultPathTranslator.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.path; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.model.Build; +import org.apache.maven.model.Model; +import org.apache.maven.model.Reporting; +import org.apache.maven.model.Resource; + +/** + * DefaultPathTranslator + */ +@Deprecated +@Named +@Singleton +public class DefaultPathTranslator implements PathTranslator { + private static final String[] BASEDIR_EXPRESSIONS = {"${basedir}", "${pom.basedir}", "${project.basedir}"}; + + @Override + public void alignToBaseDirectory(Model model, File basedir) { + if (basedir == null) { + return; + } + + Build build = model.getBuild(); + + if (build != null) { + build.setDirectory(alignToBaseDirectory(build.getDirectory(), basedir)); + + build.setSourceDirectory(alignToBaseDirectory(build.getSourceDirectory(), basedir)); + + build.setTestSourceDirectory(alignToBaseDirectory(build.getTestSourceDirectory(), basedir)); + + for (Resource resource : build.getResources()) { + resource.setDirectory(alignToBaseDirectory(resource.getDirectory(), basedir)); + } + + for (Resource resource : build.getTestResources()) { + resource.setDirectory(alignToBaseDirectory(resource.getDirectory(), basedir)); + } + + if (build.getFilters() != null) { + List filters = new ArrayList<>(); + for (String filter : build.getFilters()) { + filters.add(alignToBaseDirectory(filter, basedir)); + } + build.setFilters(filters); + } + + build.setOutputDirectory(alignToBaseDirectory(build.getOutputDirectory(), basedir)); + + build.setTestOutputDirectory(alignToBaseDirectory(build.getTestOutputDirectory(), basedir)); + } + + Reporting reporting = model.getReporting(); + + if (reporting != null) { + reporting.setOutputDirectory(alignToBaseDirectory(reporting.getOutputDirectory(), basedir)); + } + } + + @Override + public String alignToBaseDirectory(String path, File basedir) { + if (basedir == null) { + return path; + } + + if (path == null) { + return null; + } + + String s = stripBasedirToken(path); + + File file = new File(s); + if (file.isAbsolute()) { + // path was already absolute, just normalize file separator and we're done + s = file.getPath(); + } else if (file.getPath().startsWith(File.separator)) { + // drive-relative Windows path, don't align with project directory but with drive root + s = file.getAbsolutePath(); + } else { + // an ordinary relative path, align with project directory + s = new File(new File(basedir, s).toURI().normalize()).getAbsolutePath(); + } + + return s; + } + + private String stripBasedirToken(String s) { + if (s != null) { + String basedirExpr = null; + for (String expression : BASEDIR_EXPRESSIONS) { + if (s.startsWith(expression)) { + basedirExpr = expression; + break; + } + } + + if (basedirExpr != null) { + if (s.length() > basedirExpr.length()) { + // Take out basedir expression and the leading slash + s = chopLeadingFileSeparator(s.substring(basedirExpr.length())); + } else { + s = "."; + } + } + } + + return s; + } + + /** + * Removes the leading directory separator from the specified filesystem path (if any). For platform-independent + * behavior, this method accepts both the forward slash and the backward slash as separator. + * + * @param path The filesystem path, may be null. + * @return The altered filesystem path or null if the input path was null. + */ + private String chopLeadingFileSeparator(String path) { + if (path != null) { + if (path.startsWith("/") || path.startsWith("\\")) { + path = path.substring(1); + } + } + return path; + } + + @Override + public void unalignFromBaseDirectory(Model model, File basedir) { + if (basedir == null) { + return; + } + + Build build = model.getBuild(); + + if (build != null) { + build.setDirectory(unalignFromBaseDirectory(build.getDirectory(), basedir)); + + build.setSourceDirectory(unalignFromBaseDirectory(build.getSourceDirectory(), basedir)); + + build.setTestSourceDirectory(unalignFromBaseDirectory(build.getTestSourceDirectory(), basedir)); + + for (Resource resource : build.getResources()) { + resource.setDirectory(unalignFromBaseDirectory(resource.getDirectory(), basedir)); + } + + for (Resource resource : build.getTestResources()) { + resource.setDirectory(unalignFromBaseDirectory(resource.getDirectory(), basedir)); + } + + if (build.getFilters() != null) { + List filters = new ArrayList<>(); + for (String filter : build.getFilters()) { + filters.add(unalignFromBaseDirectory(filter, basedir)); + } + build.setFilters(filters); + } + + build.setOutputDirectory(unalignFromBaseDirectory(build.getOutputDirectory(), basedir)); + + build.setTestOutputDirectory(unalignFromBaseDirectory(build.getTestOutputDirectory(), basedir)); + } + + Reporting reporting = model.getReporting(); + + if (reporting != null) { + reporting.setOutputDirectory(unalignFromBaseDirectory(reporting.getOutputDirectory(), basedir)); + } + } + + @Override + public String unalignFromBaseDirectory(String path, File basedir) { + if (basedir == null) { + return path; + } + + if (path == null) { + return null; + } + + path = path.trim(); + + String base = basedir.getAbsolutePath(); + if (path.startsWith(base)) { + path = chopLeadingFileSeparator(path.substring(base.length())); + } + + if (path.isEmpty()) { + path = "."; + } + + if (!new File(path).isAbsolute()) { + path = path.replace('\\', '/'); + } + + return path; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/path/PathTranslator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/path/PathTranslator.java new file mode 100644 index 000000000000..c345412fd5b4 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/path/PathTranslator.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.path; + +import java.io.File; + +import org.apache.maven.model.Model; + +/** + */ +@Deprecated +public interface PathTranslator { + String ROLE = PathTranslator.class.getName(); + + void alignToBaseDirectory(Model model, File basedir); + + String alignToBaseDirectory(String path, File basedir); + + void unalignFromBaseDirectory(Model model, File basedir); + + String unalignFromBaseDirectory(String directory, File basedir); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/validation/DefaultModelValidator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/DefaultModelValidator.java new file mode 100644 index 000000000000..59f623e5e28c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/DefaultModelValidator.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.validation; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.model.Model; +import org.apache.maven.model.building.DefaultModelBuildingRequest; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.building.ModelProblemCollector; +import org.apache.maven.model.building.ModelProblemCollectorRequest; + +/** + */ +@Deprecated +@Named +@Singleton +public class DefaultModelValidator implements ModelValidator { + + @Inject + private org.apache.maven.model.validation.ModelValidator modelValidator; + + @Override + public ModelValidationResult validate(Model model) { + ModelValidationResult result = new ModelValidationResult(); + + ModelBuildingRequest request = + new DefaultModelBuildingRequest().setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0); + + SimpleModelProblemCollector problems = new SimpleModelProblemCollector(result); + + modelValidator.validateEffectiveModel(model, request, problems); + + return result; + } + + private static class SimpleModelProblemCollector implements ModelProblemCollector { + + ModelValidationResult result; + + SimpleModelProblemCollector(ModelValidationResult result) { + this.result = result; + } + + @Override + public void add(ModelProblemCollectorRequest req) { + if (!ModelProblem.Severity.WARNING.equals(req.getSeverity())) { + result.addMessage(req.getMessage()); + } + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java new file mode 100644 index 000000000000..693227ebccf3 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.validation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + */ +@Deprecated +public class ModelValidationResult { + + /** */ + private static final String LS = System.lineSeparator(); + + /** */ + private List messages; + + public ModelValidationResult() { + messages = new ArrayList<>(); + } + + public int getMessageCount() { + return messages.size(); + } + + public String getMessage(int i) { + return messages.get(i); + } + + public List getMessages() { + return Collections.unmodifiableList(messages); + } + + public void addMessage(String message) { + messages.add(message); + } + + @Override + public String toString() { + return render(""); + } + + public String render(String indentation) { + if (messages.isEmpty()) { + return indentation + "There were no validation errors."; + } + + StringBuilder message = new StringBuilder(); + for (int i = 0; i < messages.size(); i++) { + message.append(indentation) + .append('[') + .append(i) + .append("] ") + .append(messages.get(i)) + .append(LS); + } + + return message.toString(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidator.java new file mode 100644 index 000000000000..a9bd44bf7946 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidator.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.validation; + +import org.apache.maven.model.Model; + +/** + * Checks the model for missing or invalid values. + * + */ +@Deprecated +public interface ModelValidator { + + String ROLE = ModelValidator.class.getName(); + + ModelValidationResult validate(Model model); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/reporting/MavenReportException.java b/compat/maven-compat/src/main/java/org/apache/maven/reporting/MavenReportException.java new file mode 100644 index 000000000000..f6e1fbb351e7 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/reporting/MavenReportException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.reporting; + +/** + * An exception occurring during the execution of a Maven report. + * + */ +@Deprecated +public class MavenReportException extends Exception { + public MavenReportException(String msg) { + super(msg); + } + + public MavenReportException(String msg, Exception e) { + super(msg, e); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/repository/ArtifactDoesNotExistException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactDoesNotExistException.java similarity index 75% rename from maven-core/src/main/java/org/apache/maven/repository/ArtifactDoesNotExistException.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactDoesNotExistException.java index 3e72387702e5..5624e1d99d01 100644 --- a/maven-core/src/main/java/org/apache/maven/repository/ArtifactDoesNotExistException.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactDoesNotExistException.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,20 +16,18 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository; /** * ArtifactDoesNotExistException */ -public class ArtifactDoesNotExistException - extends Exception -{ - public ArtifactDoesNotExistException( final String message ) - { - super( message ); +@Deprecated +public class ArtifactDoesNotExistException extends Exception { + public ArtifactDoesNotExistException(final String message) { + super(message); } - public ArtifactDoesNotExistException( final String message, final Throwable cause ) - { - super( message, cause ); + public ArtifactDoesNotExistException(final String message, final Throwable cause) { + super(message, cause); } } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferEvent.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferEvent.java new file mode 100644 index 000000000000..2dfe5f85b7b0 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferEvent.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.io.File; +import java.util.EventObject; + +/** + * TransferEvent is used to notify TransferListeners about progress + * in transfer of resources form/to the repository + * + */ +@Deprecated +public class ArtifactTransferEvent extends EventObject { + /** + * A transfer was attempted, but has not yet commenced. + */ + public static final int TRANSFER_INITIATED = 0; + + /** + * A transfer was started. + */ + public static final int TRANSFER_STARTED = 1; + + /** + * A transfer is completed. + */ + public static final int TRANSFER_COMPLETED = 2; + + /** + * A transfer is in progress. + */ + public static final int TRANSFER_PROGRESS = 3; + + /** + * An error occurred during transfer + */ + public static final int TRANSFER_ERROR = 4; + + /** + * Indicates GET transfer (from the repository) + */ + public static final int REQUEST_GET = 5; + + /** + * Indicates PUT transfer (to the repository) + */ + public static final int REQUEST_PUT = 6; + + private int eventType; + + private int requestType; + + private Exception exception; + + private File localFile; + + private ArtifactTransferResource artifact; + + private long transferredBytes; + + private byte[] dataBuffer; + + private int dataOffset; + + private int dataLength; + + public ArtifactTransferEvent( + String wagon, final int eventType, final int requestType, ArtifactTransferResource artifact) { + super(wagon); + + setEventType(eventType); + + setRequestType(requestType); + + this.artifact = artifact; + } + + public ArtifactTransferEvent( + String wagon, final Exception exception, final int requestType, ArtifactTransferResource artifact) { + this(wagon, TRANSFER_ERROR, requestType, artifact); + + this.exception = exception; + } + + public ArtifactTransferResource getResource() { + return artifact; + } + + /** + * @return Returns the exception. + */ + public Exception getException() { + return exception; + } + + /** + * Returns the request type. + * + * @return Returns the request type. The Request type is one of + * TransferEvent.REQUEST_GET or TransferEvent.REQUEST_PUT + */ + public int getRequestType() { + return requestType; + } + + /** + * Sets the request type + * + * @param requestType The requestType to set. + * The Request type value should be either + * TransferEvent.REQUEST_GET or TransferEvent.REQUEST_PUT. + * @throws IllegalArgumentException when + */ + public void setRequestType(final int requestType) { + switch (requestType) { + case REQUEST_PUT: + break; + case REQUEST_GET: + break; + default: + throw new IllegalArgumentException("Illegal request type: " + requestType); + } + + this.requestType = requestType; + } + + /** + * @return Returns the eventType. + */ + public int getEventType() { + return eventType; + } + + /** + * @param eventType The eventType to set. + */ + public void setEventType(final int eventType) { + switch (eventType) { + case TRANSFER_INITIATED: + break; + case TRANSFER_STARTED: + break; + case TRANSFER_COMPLETED: + break; + case TRANSFER_PROGRESS: + break; + case TRANSFER_ERROR: + break; + default: + throw new IllegalArgumentException("Illegal event type: " + eventType); + } + + this.eventType = eventType; + } + + /** + * @return Returns the local file. + */ + public File getLocalFile() { + return localFile; + } + + /** + * @param localFile The local file to set. + */ + public void setLocalFile(File localFile) { + this.localFile = localFile; + } + + public long getTransferredBytes() { + return transferredBytes; + } + + public void setTransferredBytes(long transferredBytes) { + this.transferredBytes = transferredBytes; + } + + public byte[] getDataBuffer() { + return dataBuffer; + } + + public void setDataBuffer(byte[] dataBuffer) { + this.dataBuffer = dataBuffer; + } + + public int getDataOffset() { + return dataOffset; + } + + public void setDataOffset(int dataOffset) { + this.dataOffset = dataOffset; + } + + public int getDataLength() { + return dataLength; + } + + public void setDataLength(int dataLength) { + this.dataLength = dataLength; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + + sb.append("TransferEvent["); + + switch (this.getRequestType()) { + case REQUEST_GET: + sb.append("GET"); + break; + case REQUEST_PUT: + sb.append("PUT"); + break; + default: + sb.append(this.getRequestType()); + break; + } + + sb.append('|'); + switch (this.getEventType()) { + case TRANSFER_COMPLETED: + sb.append("COMPLETED"); + break; + case TRANSFER_ERROR: + sb.append("ERROR"); + break; + case TRANSFER_INITIATED: + sb.append("INITIATED"); + break; + case TRANSFER_PROGRESS: + sb.append("PROGRESS"); + break; + case TRANSFER_STARTED: + sb.append("STARTED"); + break; + default: + sb.append(this.getEventType()); + break; + } + + sb.append('|'); + sb.append(this.getLocalFile()).append('|'); + sb.append(']'); + + return sb.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + eventType; + result = prime * result + ((exception == null) ? 0 : exception.hashCode()); + result = prime * result + ((localFile == null) ? 0 : localFile.hashCode()); + result = prime * result + requestType; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + final ArtifactTransferEvent other = (ArtifactTransferEvent) obj; + if (eventType != other.eventType) { + return false; + } + if (exception == null) { + if (other.exception != null) { + return false; + } + } else if (!exception.getClass().equals(other.exception.getClass())) { + return false; + } + if (requestType != other.requestType) { + return false; + } else { + return source.equals(other.source); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/repository/ArtifactTransferFailedException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferFailedException.java similarity index 75% rename from maven-core/src/main/java/org/apache/maven/repository/ArtifactTransferFailedException.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferFailedException.java index f35de659b7b9..530ec343a9b1 100644 --- a/maven-core/src/main/java/org/apache/maven/repository/ArtifactTransferFailedException.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferFailedException.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,21 +16,18 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository; /** * ArtifactTransferFailedException */ -public class ArtifactTransferFailedException - extends Exception -{ - public ArtifactTransferFailedException( final String message ) - { - super( message ); +@Deprecated +public class ArtifactTransferFailedException extends Exception { + public ArtifactTransferFailedException(final String message) { + super(message); } - public ArtifactTransferFailedException( final String message, final Throwable cause ) - { - super( message, cause ); + public ArtifactTransferFailedException(final String message, final Throwable cause) { + super(message, cause); } - } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferListener.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferListener.java new file mode 100644 index 000000000000..c2868815824c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +/** + * ArtifactTransferListener + */ +@Deprecated +public interface ArtifactTransferListener { + boolean isShowChecksumEvents(); + + void setShowChecksumEvents(boolean showChecksumEvents); + + void transferInitiated(ArtifactTransferEvent transferEvent); + + void transferStarted(ArtifactTransferEvent transferEvent); + + void transferProgress(ArtifactTransferEvent transferEvent); + + void transferCompleted(ArtifactTransferEvent transferEvent); +} diff --git a/maven-core/src/main/java/org/apache/maven/repository/ArtifactTransferResource.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferResource.java similarity index 96% rename from maven-core/src/main/java/org/apache/maven/repository/ArtifactTransferResource.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferResource.java index 8ed081ffc0c3..22aeba543877 100644 --- a/maven-core/src/main/java/org/apache/maven/repository/ArtifactTransferResource.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/ArtifactTransferResource.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,14 +16,14 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository; /** * Describes a resource being uploaded or downloaded by the repository system. * - * @author Benjamin Bentmann */ -public interface ArtifactTransferResource -{ +@Deprecated +public interface ArtifactTransferResource { /** * The base URL of the repository, e.g. "http://repo1.maven.org/maven2/". Unless the URL is unknown, it will be @@ -62,5 +60,4 @@ public interface ArtifactTransferResource * @return The timestamp when the transfer of this artifact was started. */ long getTransferStartTime(); - } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java new file mode 100644 index 000000000000..ace636cbcc02 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.settings.Mirror; + +/** + * DefaultMirrorSelector + */ +@Named +@Singleton +@Deprecated +public class DefaultMirrorSelector implements MirrorSelector { + + private static final String WILDCARD = "*"; + + private static final String EXTERNAL_WILDCARD = "external:*"; + + private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*"; + + @Override + public Mirror getMirror(ArtifactRepository repository, List mirrors) { + String repoId = repository.getId(); + + if (repoId != null && mirrors != null) { + for (Mirror mirror : mirrors) { + if (repoId.equals(mirror.getMirrorOf()) && matchesLayout(repository, mirror)) { + return mirror; + } + } + + for (Mirror mirror : mirrors) { + if (matchPattern(repository, mirror.getMirrorOf()) && matchesLayout(repository, mirror)) { + return mirror; + } + } + } + + return null; + } + + /** + * This method checks if the pattern matches the originalRepository. Valid patterns: + *
      + *
    • {@code *} = everything,
    • + *
    • {@code external:*} = everything not on the localhost and not file based,
    • + *
    • {@code external:http:*} = any repository not on the localhost using HTTP,
    • + *
    • {@code repo,repo1} = {@code repo} or {@code repo1},
    • + *
    • {@code *,!repo1} = everything except {@code repo1}.
    • + *
    + * + * @param originalRepository to compare for a match. + * @param pattern used for match. + * @return true if the repository is a match to this pattern. + */ + static boolean matchPattern(ArtifactRepository originalRepository, String pattern) { + boolean result = false; + String originalId = originalRepository.getId(); + + // simple checks first to short circuit processing below. + if (WILDCARD.equals(pattern) || pattern.equals(originalId)) { + result = true; + } else { + // process the list + String[] repos = pattern.split(","); + for (String repo : repos) { + repo = repo.trim(); + // see if this is a negative match + if (repo.length() > 1 && repo.startsWith("!")) { + if (repo.substring(1).equals(originalId)) { + // explicitly exclude. Set result and stop processing. + result = false; + break; + } + } + // check for exact match + else if (repo.equals(originalId)) { + result = true; + break; + } + // check for external:* + else if (EXTERNAL_WILDCARD.equals(repo) && isExternalRepo(originalRepository)) { + result = true; + // don't stop processing in case a future segment explicitly excludes this repo + } + // check for external:http:* + else if (EXTERNAL_HTTP_WILDCARD.equals(repo) && isExternalHttpRepo(originalRepository)) { + result = true; + // don't stop processing in case a future segment explicitly excludes this repo + } else if (WILDCARD.equals(repo)) { + result = true; + // don't stop processing in case a future segment explicitly excludes this repo + } + } + } + return result; + } + + /** + * Checks the URL to see if this repository refers to an external repository + * + * @param originalRepository + * @return true if external. + */ + static boolean isExternalRepo(ArtifactRepository originalRepository) { + try { + URL url = new URL(originalRepository.getUrl()); + return !(isLocal(url.getHost()) || url.getProtocol().equals("file")); + } catch (MalformedURLException e) { + // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it + return false; + } + } + + private static boolean isLocal(String host) { + return "localhost".equals(host) || "127.0.0.1".equals(host); + } + + /** + * Checks the URL to see if this repository refers to a non-localhost repository using HTTP. + * + * @param originalRepository + * @return true if external. + */ + static boolean isExternalHttpRepo(ArtifactRepository originalRepository) { + try { + URL url = new URL(originalRepository.getUrl()); + return ("http".equalsIgnoreCase(url.getProtocol()) + || "dav".equalsIgnoreCase(url.getProtocol()) + || "dav:http".equalsIgnoreCase(url.getProtocol()) + || "dav+http".equalsIgnoreCase(url.getProtocol())) + && !isLocal(url.getHost()); + } catch (MalformedURLException e) { + // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it + return false; + } + } + + static boolean matchesLayout(ArtifactRepository repository, Mirror mirror) { + return matchesLayout(RepositoryUtils.getLayout(repository), mirror.getMirrorOfLayouts()); + } + + /** + * Checks whether the layouts configured for a mirror match with the layout of the repository. + * + * @param repoLayout The layout of the repository, may be {@code null}. + * @param mirrorLayout The layouts supported by the mirror, may be {@code null}. + * @return {@code true} if the layouts associated with the mirror match the layout of the original repository, + * {@code false} otherwise. + */ + static boolean matchesLayout(String repoLayout, String mirrorLayout) { + boolean result = false; + + // simple checks first to short circuit processing below. + if ((mirrorLayout == null || mirrorLayout.isEmpty()) || WILDCARD.equals(mirrorLayout)) { + result = true; + } else if (mirrorLayout.equals(repoLayout)) { + result = true; + } else { + // process the list + String[] layouts = mirrorLayout.split(","); + for (String layout : layouts) { + // see if this is a negative match + if (layout.length() > 1 && layout.startsWith("!")) { + if (layout.substring(1).equals(repoLayout)) { + // explicitly exclude. Set result and stop processing. + result = false; + break; + } + } + // check for exact match + else if (layout.equals(repoLayout)) { + result = true; + break; + } else if (WILDCARD.equals(layout)) { + result = true; + // don't stop processing in case a future segment explicitly excludes this repo + } + } + } + + return result; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/DelegatingLocalArtifactRepository.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/DelegatingLocalArtifactRepository.java new file mode 100644 index 000000000000..30827c6a4d36 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/DelegatingLocalArtifactRepository.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; + +/** + * Delegating local artifact repository chains the reactor, IDE workspace + * and user local repository. + */ +@Deprecated +public class DelegatingLocalArtifactRepository extends MavenArtifactRepository { + private LocalArtifactRepository buildReactor; + + private LocalArtifactRepository ideWorkspace; + + private ArtifactRepository userLocalArtifactRepository; + + public DelegatingLocalArtifactRepository(ArtifactRepository artifactRepository) { + this.userLocalArtifactRepository = artifactRepository; + } + + public void setBuildReactor(LocalArtifactRepository localRepository) { + this.buildReactor = localRepository; + } + + public void setIdeWorkspace(LocalArtifactRepository localRepository) { + this.ideWorkspace = localRepository; + } + + /** + * @deprecated instead use {@link #getIdeWorkspace()} + */ + @Deprecated + public LocalArtifactRepository getIdeWorspace() { + return ideWorkspace; + } + + public LocalArtifactRepository getIdeWorkspace() { + return getIdeWorspace(); + } + + @Override + public Artifact find(Artifact artifact) { + if (!artifact.isRelease() && buildReactor != null) { + artifact = buildReactor.find(artifact); + } + + if (!artifact.isResolved() && ideWorkspace != null) { + artifact = ideWorkspace.find(artifact); + } + + if (!artifact.isResolved()) { + artifact = userLocalArtifactRepository.find(artifact); + } + + return artifact; + } + + @Override + public List findVersions(Artifact artifact) { + Collection versions = new LinkedHashSet<>(); + + if (buildReactor != null) { + versions.addAll(buildReactor.findVersions(artifact)); + } + + if (ideWorkspace != null) { + versions.addAll(ideWorkspace.findVersions(artifact)); + } + + versions.addAll(userLocalArtifactRepository.findVersions(artifact)); + + return Collections.unmodifiableList(new ArrayList<>(versions)); + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return userLocalArtifactRepository.pathOfLocalRepositoryMetadata(metadata, repository); + } + + @Override + public String getId() { + return userLocalArtifactRepository.getId(); + } + + @Override + public String pathOf(Artifact artifact) { + return userLocalArtifactRepository.pathOf(artifact); + } + + @Override + public String getBasedir() { + return (userLocalArtifactRepository != null) ? userLocalArtifactRepository.getBasedir() : null; + } + + @Override + public ArtifactRepositoryLayout getLayout() { + return userLocalArtifactRepository.getLayout(); + } + + @Override + public ArtifactRepositoryPolicy getReleases() { + return userLocalArtifactRepository.getReleases(); + } + + @Override + public ArtifactRepositoryPolicy getSnapshots() { + return userLocalArtifactRepository.getSnapshots(); + } + + @Override + public String getKey() { + return userLocalArtifactRepository.getKey(); + } + + @Override + public String getUrl() { + return userLocalArtifactRepository.getUrl(); + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + (buildReactor == null ? 0 : buildReactor.hashCode()); + hash = hash * 31 + (ideWorkspace == null ? 0 : ideWorkspace.hashCode()); + hash = hash * 31 + (userLocalArtifactRepository == null ? 0 : userLocalArtifactRepository.hashCode()); + + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + DelegatingLocalArtifactRepository other = (DelegatingLocalArtifactRepository) obj; + + return eq(buildReactor, other.buildReactor) + && eq(ideWorkspace, other.ideWorkspace) + && eq(userLocalArtifactRepository, other.userLocalArtifactRepository); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/repository/LocalArtifactRepository.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/LocalArtifactRepository.java similarity index 87% rename from maven-core/src/main/java/org/apache/maven/repository/LocalArtifactRepository.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/LocalArtifactRepository.java index 6f1c93478b21..0a14d8bab948 100644 --- a/maven-core/src/main/java/org/apache/maven/repository/LocalArtifactRepository.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/LocalArtifactRepository.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.MavenArtifactRepository; @@ -25,12 +24,12 @@ /** * LocalArtifactRepository */ -public abstract class LocalArtifactRepository - extends MavenArtifactRepository -{ +@Deprecated +public abstract class LocalArtifactRepository extends MavenArtifactRepository { public static final String IDE_WORKSPACE = "ide-workspace"; - public abstract Artifact find( Artifact artifact ); + @Override + public abstract Artifact find(Artifact artifact); public abstract boolean hasLocalMetadata(); } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/LocalRepositoryNotAccessibleException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/LocalRepositoryNotAccessibleException.java new file mode 100644 index 000000000000..921c07d43cf6 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/LocalRepositoryNotAccessibleException.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.io.IOException; + +/** + * Signals a failure to store files within the local repository. + * + */ +@Deprecated +public class LocalRepositoryNotAccessibleException extends IOException { + + public LocalRepositoryNotAccessibleException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + public LocalRepositoryNotAccessibleException(String message) { + super(message); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/MavenArtifactMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/MavenArtifactMetadata.java new file mode 100644 index 000000000000..d84e67a03ab8 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/MavenArtifactMetadata.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +/** + * + * + * + */ +@Deprecated +public class MavenArtifactMetadata { + public static final String DEFAULT_TYPE = "jar"; + + String groupId; + String artifactId; + String version; + String classifier; + String type; + String scope; + + transient Object datum; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getClassifier() { + return classifier; + } + + public void setClassifier(String classifier) { + this.classifier = classifier; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Object getDatum() { + return datum; + } + + public void setDatum(Object datum) { + this.datum = datum; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + @Override + public String toString() { + return getGroupId() + ":" + getArtifactId() + ":" + getVersion() + ":" + + (getClassifier() == null ? "" : getClassifier()) + ":" + + (getType() == null ? DEFAULT_TYPE : getType()); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataGraph.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataGraph.java new file mode 100644 index 000000000000..d6b7c9205b96 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataGraph.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * This is the main graph data structure used by the RepositorySystem to present tree and graph objects. + * + * + */ +@Deprecated +public class MetadataGraph { + /** all graph nodes */ + Collection nodes; + + /** entry point for tree-like structures */ + MetadataGraphNode entry; + + public MetadataGraph(MetadataGraphNode entry) { + this(); + + this.entry = entry; + } + + public MetadataGraph() { + nodes = new ArrayList<>(64); + } + + public void addNode(MetadataGraphNode node) { + nodes.add(node); + } + + /** + * find a node by the GAV (metadata) + * + * @param md + */ + public MetadataGraphNode findNode(MavenArtifactMetadata md) { + for (MetadataGraphNode mgn : nodes) { + if (mgn.metadata.equals(md)) { + return mgn; + } + } + + MetadataGraphNode node = new MetadataGraphNode(md); + addNode(node); + return node; + } + + /** + * getter + */ + public MetadataGraphNode getEntry() { + return entry; + } + + /** + * getter + */ + public Collection getNodes() { + return nodes; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataGraphNode.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataGraphNode.java new file mode 100644 index 000000000000..c67a55550f1f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataGraphNode.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.util.ArrayList; +import java.util.List; + +/** + * MetadataGraph node - as it's a directed graph - holds adjacency lists for incident and exident nodes + * + * + */ +@Deprecated +public class MetadataGraphNode { + /** node payload */ + MavenArtifactMetadata metadata; + + /** nodes, incident to this (depend on me) */ + List inNodes; + + /** nodes, exident to this (I depend on) */ + List exNodes; + + public MetadataGraphNode() { + inNodes = new ArrayList<>(4); + exNodes = new ArrayList<>(8); + } + + public MetadataGraphNode(MavenArtifactMetadata metadata) { + this(); + this.metadata = metadata; + } + + public MetadataGraphNode addIncident(MetadataGraphNode node) { + inNodes.add(node); + return this; + } + + public MetadataGraphNode addExident(MetadataGraphNode node) { + exNodes.add(node); + return this; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (MetadataGraphNode.class.isAssignableFrom(obj.getClass())) { + MetadataGraphNode node2 = (MetadataGraphNode) obj; + + if (node2.metadata == null) { + return metadata == null; + } + + return metadata != null && metadata.toString().equals(node2.metadata.toString()); + } else { + return super.equals(obj); + } + } + + @Override + public int hashCode() { + if (metadata == null) { + return super.hashCode(); + } + + return metadata.toString().hashCode(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataResolutionRequest.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataResolutionRequest.java new file mode 100644 index 000000000000..75ae3047acae --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataResolutionRequest.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * + * + * + */ +@Deprecated +public class MetadataResolutionRequest { + private MavenArtifactMetadata mad; + + private String scope; + + // Needs to go away + private Set artifactDependencies; + + private ArtifactRepository localRepository; + + private List remoteRepositories; + + // This is like a filter but overrides all transitive versions + private Map managedVersionMap; + + /** result type - flat list; the default */ + private boolean asList = true; + + /** result type - dirty tree */ + private boolean asDirtyTree = false; + + /** result type - resolved tree */ + private boolean asResolvedTree = false; + + /** result type - graph */ + private boolean asGraph = false; + + public MetadataResolutionRequest() {} + + public MetadataResolutionRequest( + MavenArtifactMetadata md, ArtifactRepository localRepository, List remoteRepositories) { + this.mad = md; + this.localRepository = localRepository; + this.remoteRepositories = remoteRepositories; + } + + public MavenArtifactMetadata getArtifactMetadata() { + return mad; + } + + public MetadataResolutionRequest setArtifactMetadata(MavenArtifactMetadata md) { + this.mad = md; + + return this; + } + + public MetadataResolutionRequest setArtifactDependencies(Set artifactDependencies) { + this.artifactDependencies = artifactDependencies; + + return this; + } + + public Set getArtifactDependencies() { + return artifactDependencies; + } + + public ArtifactRepository getLocalRepository() { + return localRepository; + } + + public MetadataResolutionRequest setLocalRepository(ArtifactRepository localRepository) { + this.localRepository = localRepository; + + return this; + } + + /** + * @deprecated instead use {@link #getRemoteRepositories()} + */ + @Deprecated + public List getRemoteRepostories() { + return remoteRepositories; + } + + public List getRemoteRepositories() { + return getRemoteRepostories(); + } + + /** + * @deprecated instead use {@link #setRemoteRepositories(List)} + */ + @Deprecated + public MetadataResolutionRequest setRemoteRepostories(List remoteRepositories) { + this.remoteRepositories = remoteRepositories; + + return this; + } + + public MetadataResolutionRequest setRemoteRepositories(List remoteRepositories) { + return setRemoteRepostories(remoteRepositories); + } + + public Map getManagedVersionMap() { + return managedVersionMap; + } + + public MetadataResolutionRequest setManagedVersionMap(Map managedVersionMap) { + this.managedVersionMap = managedVersionMap; + + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder() + .append("REQUEST: ") + .append("\n") + .append("artifact: ") + .append(mad) + .append("\n") + .append(artifactDependencies) + .append("\n") + .append("localRepository: ") + .append(localRepository) + .append("\n") + .append("remoteRepositories: ") + .append(remoteRepositories) + .append("\n"); + + return sb.toString(); + } + + public boolean isAsList() { + return asList; + } + + public MetadataResolutionRequest setAsList(boolean asList) { + this.asList = asList; + return this; + } + + public boolean isAsDirtyTree() { + return asDirtyTree; + } + + public MetadataResolutionRequest setAsDirtyTree(boolean asDirtyTree) { + this.asDirtyTree = asDirtyTree; + return this; + } + + public boolean isAsResolvedTree() { + return asResolvedTree; + } + + public MetadataResolutionRequest setAsResolvedTree(boolean asResolvedTree) { + this.asResolvedTree = asResolvedTree; + return this; + } + + public boolean isAsGraph() { + return asGraph; + } + + public MetadataResolutionRequest setAsGraph(boolean asGraph) { + this.asGraph = asGraph; + return this; + } + + public MetadataResolutionRequest setScope(String scope) { + this.scope = scope; + return this; + } + + public String getScope() { + return scope; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataResolutionResult.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataResolutionResult.java new file mode 100644 index 000000000000..5e1347c73f7a --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/MetadataResolutionResult.java @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.CyclicDependencyException; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; + +@Deprecated +public class MetadataResolutionResult { + private Artifact originatingArtifact; + + private List missingArtifacts; + + // Exceptions + + private List exceptions; + + private List versionRangeViolations; + + private List metadataResolutionExceptions; + + private List circularDependencyExceptions; + + private List errorArtifactExceptions; + + // file system errors + + private List repositories; + + private Set requestedArtifacts; + + private Set artifacts; + + private MetadataGraph resolvedTree; + + public Artifact getOriginatingArtifact() { + return originatingArtifact; + } + + public MetadataResolutionResult listOriginatingArtifact(final Artifact originatingArtifact) { + this.originatingArtifact = originatingArtifact; + + return this; + } + + public void addArtifact(Artifact artifact) { + if (artifacts == null) { + artifacts = new LinkedHashSet<>(); + } + + artifacts.add(artifact); + } + + public Set getArtifacts() { + return artifacts; + } + + public void addRequestedArtifact(Artifact artifact) { + if (requestedArtifacts == null) { + requestedArtifacts = new LinkedHashSet<>(); + } + + requestedArtifacts.add(artifact); + } + + public Set getRequestedArtifacts() { + return requestedArtifacts; + } + + public boolean hasMissingArtifacts() { + return missingArtifacts != null && !missingArtifacts.isEmpty(); + } + + public List getMissingArtifacts() { + return missingArtifacts == null ? Collections.emptyList() : Collections.unmodifiableList(missingArtifacts); + } + + public MetadataResolutionResult addMissingArtifact(Artifact artifact) { + missingArtifacts = initList(missingArtifacts); + + missingArtifacts.add(artifact); + + return this; + } + + public MetadataResolutionResult setUnresolvedArtifacts(final List unresolvedArtifacts) { + this.missingArtifacts = unresolvedArtifacts; + + return this; + } + + // ------------------------------------------------------------------------ + // Exceptions + // ------------------------------------------------------------------------ + + public boolean hasExceptions() { + return exceptions != null && !exceptions.isEmpty(); + } + + public List getExceptions() { + return exceptions == null ? Collections.emptyList() : Collections.unmodifiableList(exceptions); + } + + // ------------------------------------------------------------------------ + // Version Range Violations + // ------------------------------------------------------------------------ + + public boolean hasVersionRangeViolations() { + return versionRangeViolations != null; + } + + /** + * TODO this needs to accept a {@link OverConstrainedVersionException} as returned by + * {@link #getVersionRangeViolation(int)} but it's not used like that in + * {@link org.apache.maven.repository.legacy.resolver.DefaultLegacyArtifactCollector} + */ + public MetadataResolutionResult addVersionRangeViolation(Exception e) { + versionRangeViolations = initList(versionRangeViolations); + + versionRangeViolations.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public OverConstrainedVersionException getVersionRangeViolation(int i) { + return (OverConstrainedVersionException) versionRangeViolations.get(i); + } + + public List getVersionRangeViolations() { + return versionRangeViolations == null + ? Collections.emptyList() + : Collections.unmodifiableList(versionRangeViolations); + } + + // ------------------------------------------------------------------------ + // Metadata Resolution Exceptions: ArtifactResolutionExceptions + // ------------------------------------------------------------------------ + + public boolean hasMetadataResolutionExceptions() { + return metadataResolutionExceptions != null; + } + + public MetadataResolutionResult addMetadataResolutionException(ArtifactResolutionException e) { + metadataResolutionExceptions = initList(metadataResolutionExceptions); + + metadataResolutionExceptions.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public ArtifactResolutionException getMetadataResolutionException(int i) { + return metadataResolutionExceptions.get(i); + } + + public List getMetadataResolutionExceptions() { + return metadataResolutionExceptions == null + ? Collections.emptyList() + : Collections.unmodifiableList(metadataResolutionExceptions); + } + + // ------------------------------------------------------------------------ + // ErrorArtifactExceptions: ArtifactResolutionExceptions + // ------------------------------------------------------------------------ + + public boolean hasErrorArtifactExceptions() { + return errorArtifactExceptions != null; + } + + public MetadataResolutionResult addError(Exception e) { + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public List getErrorArtifactExceptions() { + if (errorArtifactExceptions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(errorArtifactExceptions); + } + + // ------------------------------------------------------------------------ + // Circular Dependency Exceptions + // ------------------------------------------------------------------------ + + public boolean hasCircularDependencyExceptions() { + return circularDependencyExceptions != null; + } + + public MetadataResolutionResult addCircularDependencyException(CyclicDependencyException e) { + circularDependencyExceptions = initList(circularDependencyExceptions); + + circularDependencyExceptions.add(e); + + exceptions = initList(exceptions); + + exceptions.add(e); + + return this; + } + + public CyclicDependencyException getCircularDependencyException(int i) { + return circularDependencyExceptions.get(i); + } + + public List getCircularDependencyExceptions() { + if (circularDependencyExceptions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(circularDependencyExceptions); + } + + // ------------------------------------------------------------------------ + // Repositories + // ------------------------------------------------------------------------ + + public List getRepositories() { + if (repositories == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(repositories); + } + + public MetadataResolutionResult setRepositories(final List repositories) { + this.repositories = repositories; + + return this; + } + + // + // Internal + // + + private List initList(final List l) { + if (l == null) { + return new ArrayList<>(); + } + return l; + } + + @Override + public String toString() { + if (artifacts == null) { + return ""; + } + StringBuilder sb = new StringBuilder(256); + int i = 1; + sb.append("---------\n"); + sb.append(artifacts.size()).append('\n'); + for (Artifact a : artifacts) { + sb.append(i).append(' ').append(a).append('\n'); + i++; + } + sb.append("---------\n"); + return sb.toString(); + } + + public MetadataGraph getResolvedTree() { + return resolvedTree; + } + + public void setResolvedTree(MetadataGraph resolvedTree) { + this.resolvedTree = resolvedTree; + } +} diff --git a/maven-compat/src/main/java/org/apache/maven/repository/MirrorSelector.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/MirrorSelector.java similarity index 90% rename from maven-compat/src/main/java/org/apache/maven/repository/MirrorSelector.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/MirrorSelector.java index c15899ed44b6..eb958ae4864f 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/MirrorSelector.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/MirrorSelector.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository; import java.util.List; @@ -27,10 +26,9 @@ /** * Handles the selection of mirrors for repositories. * - * @author Benjamin Bentmann */ -public interface MirrorSelector -{ +@Deprecated +public interface MirrorSelector { /** * Determines the mirror for the specified repository. @@ -39,6 +37,5 @@ public interface MirrorSelector * @param mirrors The available mirrors, may be {@code null}. * @return The mirror specification for the repository or {@code null} if no mirror matched. */ - Mirror getMirror( ArtifactRepository repository, List mirrors ); - + Mirror getMirror(ArtifactRepository repository, List mirrors); } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/RepositorySystem.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/RepositorySystem.java new file mode 100644 index 000000000000..789c2cf981bc --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/RepositorySystem.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.io.File; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Repository; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Server; +import org.eclipse.aether.RepositorySystemSession; + +/** + * @since 3.0-alpha + * @deprecated Use {@link MavenRepositorySystem} if needed, or maven-resolver directly, until maven 4.x api is out + */ +@Deprecated +public interface RepositorySystem { + String DEFAULT_LOCAL_REPO_ID = MavenRepositorySystem.DEFAULT_LOCAL_REPO_ID; + + @SuppressWarnings("checkstyle:constantname") + String userHome = System.getProperty("user.home"); + + @SuppressWarnings("checkstyle:constantname") + File userMavenConfigurationHome = new File(userHome, ".m2"); + + @SuppressWarnings("checkstyle:constantname") + File defaultUserLocalRepository = new File(userMavenConfigurationHome, "repository"); + + String DEFAULT_REMOTE_REPO_ID = MavenRepositorySystem.DEFAULT_REMOTE_REPO_ID; + + String DEFAULT_REMOTE_REPO_URL = MavenRepositorySystem.DEFAULT_REMOTE_REPO_ID; + + Artifact createArtifact(String groupId, String artifactId, String version, String packaging); + + Artifact createArtifact(String groupId, String artifactId, String version, String scope, String type); + + Artifact createProjectArtifact(String groupId, String artifactId, String version); + + Artifact createArtifactWithClassifier( + String groupId, String artifactId, String version, String type, String classifier); + + Artifact createPluginArtifact(Plugin plugin); + + Artifact createDependencyArtifact(Dependency dependency); + + ArtifactRepository buildArtifactRepository(Repository repository) throws InvalidRepositoryException; + + ArtifactRepository createDefaultRemoteRepository() throws InvalidRepositoryException; + + ArtifactRepository createDefaultLocalRepository() throws InvalidRepositoryException; + + ArtifactRepository createLocalRepository(File localRepository) throws InvalidRepositoryException; + + ArtifactRepository createArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases); + + /** + * Calculates the effective repositories for the given input repositories which are assumed to be already mirrored + * (if applicable). This process will essentially remove duplicate repositories by merging them into one equivalent + * repository. It is worth to point out that merging does not simply choose one of the input repositories and + * discards the others but actually combines their possibly different policies. + * + * @param repositories The original repositories, may be {@code null}. + * @return The effective repositories or {@code null} if the input was {@code null}. + */ + List getEffectiveRepositories(List repositories); + + /** + * Determines the mirror for the specified repository. + * + * @param repository The repository to determine the mirror for, must not be {@code null}. + * @param mirrors The available mirrors, may be {@code null}. + * @return The mirror specification for the repository or {@code null} if no mirror matched. + */ + Mirror getMirror(ArtifactRepository repository, List mirrors); + + /** + * Injects the mirroring information into the specified repositories. For each repository that is matched by a + * mirror, its URL and ID will be updated to match the values from the mirror specification. Repositories without a + * matching mirror will pass through unchanged. Note: This method must be called before + * {@link #injectAuthentication(List, List)} or the repositories will end up with the wrong credentials. + * + * @param repositories The repositories into which to inject the mirror information, may be {@code null}. + * @param mirrors The available mirrors, may be {@code null}. + */ + void injectMirror(List repositories, List mirrors); + + /** + * Injects the proxy information into the specified repositories. For each repository that is matched by a proxy, + * its proxy data will be set accordingly. Repositories without a matching proxy will have their proxy cleared. + * Note: This method must be called after {@link #injectMirror(List, List)} or the repositories will end up + * with the wrong proxies. + * + * @param repositories The repositories into which to inject the proxy information, may be {@code null}. + * @param proxies The available proxies, may be {@code null}. + */ + void injectProxy(List repositories, List proxies); + + /** + * Injects the authentication information into the specified repositories. For each repository that is matched by a + * server, its credentials will be updated to match the values from the server specification. Repositories without a + * matching server will have their credentials cleared. Note: This method must be called after + * {@link #injectMirror(List, List)} or the repositories will end up with the wrong credentials. + * + * @param repositories The repositories into which to inject the authentication information, may be {@code null}. + * @param servers The available servers, may be {@code null}. + */ + void injectAuthentication(List repositories, List servers); + + void injectMirror(RepositorySystemSession session, List repositories); + + void injectProxy(RepositorySystemSession session, List repositories); + + void injectAuthentication(RepositorySystemSession session, List repositories); + + ArtifactResolutionResult resolve(ArtifactResolutionRequest request); + + // Install + + // Deploy + + // Map types of artifacts + + // + // Raw file transfers + // + void publish( + ArtifactRepository repository, File source, String remotePath, ArtifactTransferListener transferListener) + throws ArtifactTransferFailedException; + + void retrieve( + ArtifactRepository repository, + File destination, + String remotePath, + ArtifactTransferListener transferListener) + throws ArtifactTransferFailedException, ArtifactDoesNotExistException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/UserLocalArtifactRepository.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/UserLocalArtifactRepository.java new file mode 100644 index 000000000000..2cc8e36c8c5a --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/UserLocalArtifactRepository.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.io.File; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * UserLocalArtifactRepository + */ +@Deprecated +public class UserLocalArtifactRepository extends LocalArtifactRepository { + private ArtifactRepository localRepository; + + public UserLocalArtifactRepository(ArtifactRepository localRepository) { + this.localRepository = localRepository; + setLayout(localRepository.getLayout()); + } + + @Override + public Artifact find(Artifact artifact) { + File artifactFile = new File(localRepository.getBasedir(), pathOf(artifact)); + + // We need to set the file here or the resolver will fail with an NPE, not fully equipped to deal + // with multiple local repository implementations yet. + artifact.setFile(artifactFile); + + return artifact; + } + + @Override + public String getId() { + return localRepository.getId(); + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return localRepository.pathOfLocalRepositoryMetadata(metadata, repository); + } + + @Override + public String pathOf(Artifact artifact) { + return localRepository.pathOf(artifact); + } + + @Override + public boolean hasLocalMetadata() { + return true; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/VersionNotFoundException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/VersionNotFoundException.java new file mode 100644 index 000000000000..4adb4eb660ac --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/VersionNotFoundException.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import java.io.File; + +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.model.Dependency; + +/** + * Thrown if a dependency has an invalid version. + * + */ +@Deprecated +public class VersionNotFoundException extends Exception { + private Dependency dependency; + + private String projectId; + private File pomFile; + private InvalidVersionSpecificationException cause; + + public VersionNotFoundException( + String projectId, Dependency dependency, File pomFile, InvalidVersionSpecificationException cause) { + super( + projectId + ", " + formatLocationInPom(dependency) + " " + dependency.getVersion() + ", pom file " + + pomFile, + cause); + + this.projectId = projectId; + + this.pomFile = pomFile; + + this.cause = cause; + + this.dependency = dependency; + } + + private static String formatLocationInPom(Dependency dependency) { + return "Dependency: " + ArtifactUtils.versionlessKey(dependency.getGroupId(), dependency.getArtifactId()); + } + + public Dependency getDependency() { + return dependency; + } + + public String getProjectId() { + return projectId; + } + + public File getPomFile() { + return pomFile; + } + + public InvalidVersionSpecificationException getCauseException() { + return cause; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/ChecksumFailedException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/ChecksumFailedException.java new file mode 100644 index 000000000000..ea375a3fbb33 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/ChecksumFailedException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import org.apache.maven.wagon.TransferFailedException; + +/** + * Occurs when a download checksum fails. + * + */ +@Deprecated +public class ChecksumFailedException extends TransferFailedException { + public ChecksumFailedException(String s) { + super(s); + } + + public ChecksumFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManager.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManager.java new file mode 100644 index 000000000000..d43758c99773 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManager.java @@ -0,0 +1,336 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.util.Date; +import java.util.Properties; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.Authentication; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; +import org.apache.maven.repository.Proxy; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.logging.Logger; + +/** + * DefaultUpdateCheckManager + */ +@Named +@Singleton +@Deprecated +public class DefaultUpdateCheckManager extends AbstractLogEnabled implements UpdateCheckManager { + + private static final String ERROR_KEY_SUFFIX = ".error"; + + public DefaultUpdateCheckManager() {} + + public DefaultUpdateCheckManager(Logger logger) { + enableLogging(logger); + } + + public static final String LAST_UPDATE_TAG = ".lastUpdated"; + + private static final String TOUCHFILE_NAME = "resolver-status.properties"; + + @Override + public boolean isUpdateRequired(Artifact artifact, ArtifactRepository repository) { + File file = artifact.getFile(); + + ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases(); + + if (!policy.isEnabled()) { + if (getLogger().isDebugEnabled()) { + getLogger() + .debug("Skipping update check for " + artifact + " (" + file + ") from " + repository.getId() + + " (" + repository.getUrl() + ")"); + } + + return false; + } + + if (getLogger().isDebugEnabled()) { + getLogger() + .debug("Determining update check for " + artifact + " (" + file + ") from " + repository.getId() + + " (" + repository.getUrl() + ")"); + } + + if (file == null) { + // TODO throw something instead? + return true; + } + + Date lastCheckDate; + + if (file.exists()) { + lastCheckDate = new Date(file.lastModified()); + } else { + File touchfile = getTouchfile(artifact); + lastCheckDate = readLastUpdated(touchfile, getRepositoryKey(repository)); + } + + return (lastCheckDate == null) || policy.checkOutOfDate(lastCheckDate); + } + + @Override + public boolean isUpdateRequired(RepositoryMetadata metadata, ArtifactRepository repository, File file) { + // Here, we need to determine which policy to use. Release updateInterval will be used when + // the metadata refers to a release artifact or meta-version, and snapshot updateInterval will be used when + // it refers to a snapshot artifact or meta-version. + // NOTE: Release metadata includes version information about artifacts that have been released, to allow + // meta-versions like RELEASE and LATEST to resolve, and also to allow retrieval of the range of valid, released + // artifacts available. + ArtifactRepositoryPolicy policy = metadata.getPolicy(repository); + + if (!policy.isEnabled()) { + if (getLogger().isDebugEnabled()) { + getLogger() + .debug("Skipping update check for " + metadata.getKey() + " (" + file + ") from " + + repository.getId() + " (" + repository.getUrl() + ")"); + } + + return false; + } + + if (getLogger().isDebugEnabled()) { + getLogger() + .debug("Determining update check for " + metadata.getKey() + " (" + file + ") from " + + repository.getId() + " (" + repository.getUrl() + ")"); + } + + if (file == null) { + // TODO throw something instead? + return true; + } + + Date lastCheckDate = readLastUpdated(metadata, repository, file); + + return (lastCheckDate == null) || policy.checkOutOfDate(lastCheckDate); + } + + private Date readLastUpdated(RepositoryMetadata metadata, ArtifactRepository repository, File file) { + File touchfile = getTouchfile(metadata, file); + + String key = getMetadataKey(repository, file); + + return readLastUpdated(touchfile, key); + } + + @Override + public String getError(Artifact artifact, ArtifactRepository repository) { + File touchFile = getTouchfile(artifact); + return getError(touchFile, getRepositoryKey(repository)); + } + + @Override + public void touch(Artifact artifact, ArtifactRepository repository, String error) { + File file = artifact.getFile(); + + File touchfile = getTouchfile(artifact); + + if (file.exists()) { + touchfile.delete(); + } else { + writeLastUpdated(touchfile, getRepositoryKey(repository), error); + } + } + + @Override + public void touch(RepositoryMetadata metadata, ArtifactRepository repository, File file) { + File touchfile = getTouchfile(metadata, file); + + String key = getMetadataKey(repository, file); + + writeLastUpdated(touchfile, key, null); + } + + String getMetadataKey(ArtifactRepository repository, File file) { + return repository.getId() + '.' + file.getName() + LAST_UPDATE_TAG; + } + + String getRepositoryKey(ArtifactRepository repository) { + StringBuilder buffer = new StringBuilder(256); + + Proxy proxy = repository.getProxy(); + if (proxy != null) { + if (proxy.getUserName() != null) { + int hash = (proxy.getUserName() + proxy.getPassword()).hashCode(); + buffer.append(hash).append('@'); + } + buffer.append(proxy.getHost()).append(':').append(proxy.getPort()).append('>'); + } + + // consider the username&password because a repo manager might block artifacts depending on authorization + Authentication auth = repository.getAuthentication(); + if (auth != null) { + int hash = (auth.getUsername() + auth.getPassword()).hashCode(); + buffer.append(hash).append('@'); + } + + // consider the URL (instead of the id) as this most closely relates to the contents in the repo + buffer.append(repository.getUrl()); + + return buffer.toString(); + } + + private void writeLastUpdated(File touchfile, String key, String error) { + synchronized (touchfile.getAbsolutePath().intern()) { + if (!touchfile.getParentFile().exists() + && !touchfile.getParentFile().mkdirs()) { + getLogger() + .debug("Failed to create directory: " + touchfile.getParent() + + " for tracking artifact metadata resolution."); + return; + } + + FileChannel channel = null; + FileLock lock = null; + try { + Properties props = new Properties(); + + channel = new RandomAccessFile(touchfile, "rw").getChannel(); + lock = channel.lock(); + + if (touchfile.canRead()) { + getLogger().debug("Reading resolution-state from: " + touchfile); + props.load(Channels.newInputStream(channel)); + } + + props.setProperty(key, Long.toString(System.currentTimeMillis())); + + if (error != null) { + props.setProperty(key + ERROR_KEY_SUFFIX, error); + } else { + props.remove(key + ERROR_KEY_SUFFIX); + } + + getLogger().debug("Writing resolution-state to: " + touchfile); + channel.truncate(0); + props.store(Channels.newOutputStream(channel), "Last modified on: " + new Date()); + + lock.release(); + lock = null; + + channel.close(); + channel = null; + } catch (IOException e) { + getLogger() + .debug( + "Failed to record lastUpdated information for resolution.\nFile: " + touchfile + + "; key: " + key, + e); + } finally { + if (lock != null) { + try { + lock.release(); + } catch (IOException e) { + getLogger() + .debug("Error releasing exclusive lock for resolution tracking file: " + touchfile, e); + } + } + + if (channel != null) { + try { + channel.close(); + } catch (IOException e) { + getLogger().debug("Error closing FileChannel for resolution tracking file: " + touchfile, e); + } + } + } + } + } + + Date readLastUpdated(File touchfile, String key) { + getLogger().debug("Searching for " + key + " in resolution tracking file."); + + Properties props = read(touchfile); + if (props != null) { + String rawVal = props.getProperty(key); + if (rawVal != null) { + try { + return new Date(Long.parseLong(rawVal)); + } catch (NumberFormatException e) { + getLogger().debug("Cannot parse lastUpdated date: '" + rawVal + "'. Ignoring.", e); + } + } + } + return null; + } + + private String getError(File touchFile, String key) { + Properties props = read(touchFile); + if (props != null) { + return props.getProperty(key + ERROR_KEY_SUFFIX); + } + return null; + } + + private Properties read(File touchfile) { + if (!touchfile.canRead()) { + getLogger().debug("Skipped unreadable resolution tracking file: " + touchfile); + return null; + } + + synchronized (touchfile.getAbsolutePath().intern()) { + try { + Properties props = new Properties(); + + try (FileInputStream in = new FileInputStream(touchfile)) { + try (FileLock lock = in.getChannel().lock(0, Long.MAX_VALUE, true)) { + getLogger().debug("Reading resolution-state from: " + touchfile); + props.load(in); + + return props; + } + } + + } catch (IOException e) { + getLogger().debug("Failed to read resolution tracking file: " + touchfile, e); + + return null; + } + } + } + + File getTouchfile(Artifact artifact) { + StringBuilder sb = new StringBuilder(128); + sb.append(artifact.getArtifactId()); + sb.append('-').append(artifact.getBaseVersion()); + if (artifact.getClassifier() != null) { + sb.append('-').append(artifact.getClassifier()); + } + sb.append('.').append(artifact.getType()).append(LAST_UPDATE_TAG); + return new File(artifact.getFile().getParentFile(), sb.toString()); + } + + File getTouchfile(RepositoryMetadata metadata, File file) { + return new File(file.getParent(), TOUCHFILE_NAME); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/DefaultWagonManager.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/DefaultWagonManager.java new file mode 100644 index 000000000000..49c8fa645af0 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/DefaultWagonManager.java @@ -0,0 +1,707 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.wagon.ConnectionException; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.UnsupportedProtocolException; +import org.apache.maven.wagon.Wagon; +import org.apache.maven.wagon.authentication.AuthenticationException; +import org.apache.maven.wagon.authentication.AuthenticationInfo; +import org.apache.maven.wagon.authorization.AuthorizationException; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.observers.ChecksumObserver; +import org.apache.maven.wagon.proxy.ProxyInfo; +import org.apache.maven.wagon.repository.Repository; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.logging.Logger; +import org.eclipse.aether.ConfigurationProperties; +import org.eclipse.aether.util.ConfigUtils; + +// TODO remove the update check manager +// TODO separate into retriever and publisher +// TODO remove hardcoding of checksum logic + +/** + * Manages Wagon related operations in Maven. + */ +@Named +@Singleton +@Deprecated +public class DefaultWagonManager implements WagonManager { + + private static final String[] CHECKSUM_IDS = {"md5", "sha1"}; + + /** + * have to match the CHECKSUM_IDS + */ + private static final String[] CHECKSUM_ALGORITHMS = {"MD5", "SHA-1"}; + + @Inject + private Logger logger; + + @Inject + private PlexusContainer container; + + @Inject + private UpdateCheckManager updateCheckManager; + + @Inject + private LegacySupport legacySupport; + + // + // Retriever + // + @Override + public void getArtifact( + Artifact artifact, ArtifactRepository repository, TransferListener downloadMonitor, boolean force) + throws TransferFailedException, ResourceDoesNotExistException { + String remotePath = repository.pathOf(artifact); + + ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases(); + + if (!policy.isEnabled()) { + logger.debug( + "Skipping disabled repository " + repository.getId() + " for resolution of " + artifact.getId()); + + } else if (artifact.isSnapshot() || !artifact.getFile().exists()) { + if (force || updateCheckManager.isUpdateRequired(artifact, repository)) { + logger.debug("Trying repository " + repository.getId() + " for resolution of " + artifact.getId() + + " from " + remotePath); + + try { + getRemoteFile( + repository, + artifact.getFile(), + remotePath, + downloadMonitor, + policy.getChecksumPolicy(), + false); + + updateCheckManager.touch(artifact, repository, null); + } catch (ResourceDoesNotExistException e) { + updateCheckManager.touch(artifact, repository, null); + throw e; + } catch (TransferFailedException e) { + String error = (e.getMessage() != null) + ? e.getMessage() + : e.getClass().getSimpleName(); + updateCheckManager.touch(artifact, repository, error); + throw e; + } + + logger.debug(" Artifact " + artifact.getId() + " resolved to " + artifact.getFile()); + + artifact.setResolved(true); + } else if (!artifact.getFile().exists()) { + String error = updateCheckManager.getError(artifact, repository); + if (error != null) { + throw new TransferFailedException("Failure to resolve " + remotePath + " from " + + repository.getUrl() + + " was cached in the local repository. " + + "Resolution will not be reattempted until the update interval of " + + repository.getId() + " has elapsed or updates are forced. Original error: " + error); + + } else { + throw new ResourceDoesNotExistException( + "Failure to resolve " + remotePath + " from " + repository.getUrl() + + " was cached in the local repository. " + + "Resolution will not be reattempted until the update interval of " + + repository.getId() + " has elapsed or updates are forced."); + } + } + } + } + + @Override + public void getArtifact( + Artifact artifact, + List remoteRepositories, + TransferListener downloadMonitor, + boolean force) + throws TransferFailedException, ResourceDoesNotExistException { + TransferFailedException tfe = null; + + for (ArtifactRepository repository : remoteRepositories) { + try { + getArtifact(artifact, repository, downloadMonitor, force); + + if (artifact.isResolved()) { + artifact.setRepository(repository); + break; + } + } catch (ResourceDoesNotExistException e) { + // This one we will eat when looking through remote repositories + // because we want to cycle through them all before squawking. + + logger.debug( + "Unable to find artifact " + artifact.getId() + " in repository " + repository.getId() + " (" + + repository.getUrl() + ")", + e); + + } catch (TransferFailedException e) { + tfe = e; + + String msg = "Unable to get artifact " + artifact.getId() + " from repository " + repository.getId() + + " (" + repository.getUrl() + "): " + e.getMessage(); + + if (logger.isDebugEnabled()) { + logger.warn(msg, e); + } else { + logger.warn(msg); + } + } + } + + // if it already exists locally we were just trying to force it - ignore the update + if (!artifact.getFile().exists()) { + if (tfe != null) { + throw tfe; + } else { + throw new ResourceDoesNotExistException("Unable to download the artifact from any repository"); + } + } + } + + @Override + public void getArtifactMetadata( + ArtifactMetadata metadata, ArtifactRepository repository, File destination, String checksumPolicy) + throws TransferFailedException, ResourceDoesNotExistException { + String remotePath = repository.pathOfRemoteRepositoryMetadata(metadata); + + getRemoteFile(repository, destination, remotePath, null, checksumPolicy, true); + } + + @Override + public void getArtifactMetadataFromDeploymentRepository( + ArtifactMetadata metadata, ArtifactRepository repository, File destination, String checksumPolicy) + throws TransferFailedException, ResourceDoesNotExistException { + String remotePath = repository.pathOfRemoteRepositoryMetadata(metadata); + + getRemoteFile(repository, destination, remotePath, null, checksumPolicy, true); + } + + /** + * Deal with connecting to a wagon repository taking into account authentication and proxies. + * + * @param wagon + * @param repository + * + * @throws ConnectionException + * @throws AuthenticationException + */ + private void connectWagon(Wagon wagon, ArtifactRepository repository) + throws ConnectionException, AuthenticationException { + // MNG-5509 + // See org.eclipse.aether.connector.wagon.WagonRepositoryConnector.connectWagon(Wagon) + if (legacySupport.getRepositorySession() != null) { + String userAgent = ConfigUtils.getString( + legacySupport.getRepositorySession(), null, ConfigurationProperties.USER_AGENT); + + if (userAgent == null) { + Properties headers = new Properties(); + + headers.put( + "User-Agent", + ConfigUtils.getString( + legacySupport.getRepositorySession(), "Maven", ConfigurationProperties.USER_AGENT)); + try { + Method setHttpHeaders = wagon.getClass().getMethod("setHttpHeaders", Properties.class); + setHttpHeaders.invoke(wagon, headers); + } catch (NoSuchMethodException e) { + // normal for non-http wagons + } catch (Exception e) { + logger.debug("Could not set user agent for wagon " + + wagon.getClass().getName() + ": " + e); + } + } + } + + if (repository.getProxy() != null && logger.isDebugEnabled()) { + logger.debug("Using proxy " + repository.getProxy().getHost() + ":" + + repository.getProxy().getPort() + " for " + repository.getUrl()); + } + + if (repository.getAuthentication() != null && repository.getProxy() != null) { + wagon.connect( + new Repository(repository.getId(), repository.getUrl()), + authenticationInfo(repository), + proxyInfo(repository)); + + } else if (repository.getAuthentication() != null) { + wagon.connect(new Repository(repository.getId(), repository.getUrl()), authenticationInfo(repository)); + + } else if (repository.getProxy() != null) { + wagon.connect(new Repository(repository.getId(), repository.getUrl()), proxyInfo(repository)); + } else { + wagon.connect(new Repository(repository.getId(), repository.getUrl())); + } + } + + private AuthenticationInfo authenticationInfo(ArtifactRepository repository) { + AuthenticationInfo ai = new AuthenticationInfo(); + ai.setUserName(repository.getAuthentication().getUsername()); + ai.setPassword(repository.getAuthentication().getPassword()); + return ai; + } + + private ProxyInfo proxyInfo(ArtifactRepository repository) { + ProxyInfo proxyInfo = new ProxyInfo(); + proxyInfo.setHost(repository.getProxy().getHost()); + proxyInfo.setType(repository.getProxy().getProtocol()); + proxyInfo.setPort(repository.getProxy().getPort()); + proxyInfo.setNonProxyHosts(repository.getProxy().getNonProxyHosts()); + proxyInfo.setUserName(repository.getProxy().getUserName()); + proxyInfo.setPassword(repository.getProxy().getPassword()); + return proxyInfo; + } + + @SuppressWarnings("checkstyle:methodlength") + @Override + public void getRemoteFile( + ArtifactRepository repository, + File destination, + String remotePath, + TransferListener downloadMonitor, + String checksumPolicy, + boolean force) + throws TransferFailedException, ResourceDoesNotExistException { + String protocol = repository.getProtocol(); + + Wagon wagon; + + try { + wagon = getWagon(protocol); + } catch (UnsupportedProtocolException e) { + throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e); + } + + if (downloadMonitor != null) { + wagon.addTransferListener(downloadMonitor); + } + + File temp = new File(destination + ".tmp"); + + temp.deleteOnExit(); + + boolean downloaded = false; + + try { + connectWagon(wagon, repository); + + boolean firstRun = true; + boolean retry = true; + + // this will run at most twice. The first time, the firstRun flag is turned off, and if the retry flag + // is set on the first run, it will be turned off and not re-set on the second try. This is because the + // only way the retry flag can be set is if ( firstRun == true ). + while (firstRun || retry) { + ChecksumObserver md5ChecksumObserver = null; + ChecksumObserver sha1ChecksumObserver = null; + try { + // TODO configure on repository + md5ChecksumObserver = addChecksumObserver(wagon, "MD5"); + sha1ChecksumObserver = addChecksumObserver(wagon, "SHA-1"); + + // reset the retry flag. + retry = false; + + // This should take care of creating destination directory now on + if (destination.exists() && !force) { + try { + downloaded = wagon.getIfNewer(remotePath, temp, destination.lastModified()); + + if (!downloaded) { + // prevent additional checks of this artifact until it expires again + destination.setLastModified(System.currentTimeMillis()); + } + } catch (UnsupportedOperationException e) { + // older wagons throw this. Just get() instead + wagon.get(remotePath, temp); + + downloaded = true; + } + } else { + wagon.get(remotePath, temp); + downloaded = true; + } + } finally { + wagon.removeTransferListener(md5ChecksumObserver); + wagon.removeTransferListener(sha1ChecksumObserver); + } + + if (downloaded) { + // keep the checksum files from showing up on the download monitor... + if (downloadMonitor != null) { + wagon.removeTransferListener(downloadMonitor); + } + + // try to verify the SHA-1 checksum for this file. + try { + verifyChecksum(sha1ChecksumObserver, destination, temp, remotePath, ".sha1", wagon); + } catch (ChecksumFailedException e) { + // if we catch a ChecksumFailedException, it means the transfer/read succeeded, but the + // checksum doesn't match. This could be a problem with the server (ibiblio HTTP-200 error + // page), so we'll try this up to two times. On the second try, we'll handle it as a bona-fide + // error, based on the repository's checksum checking policy. + if (firstRun) { + logger.warn("*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING"); + retry = true; + } else { + handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause()); + } + } catch (ResourceDoesNotExistException sha1TryException) { + logger.debug("SHA1 not found, trying MD5: " + sha1TryException.getMessage()); + + // if this IS NOT a ChecksumFailedException, it was a problem with transfer/read of the checksum + // file...we'll try again with the MD5 checksum. + try { + verifyChecksum(md5ChecksumObserver, destination, temp, remotePath, ".md5", wagon); + } catch (ChecksumFailedException e) { + // if we also fail to verify based on the MD5 checksum, and the checksum transfer/read + // succeeded, then we need to determine whether to retry or handle it as a failure. + if (firstRun) { + retry = true; + } else { + handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause()); + } + } catch (ResourceDoesNotExistException md5TryException) { + // this was a failed transfer, and we don't want to retry. + handleChecksumFailure( + checksumPolicy, + "Error retrieving checksum file for " + remotePath, + md5TryException); + } + } + + // reinstate the download monitor... + if (downloadMonitor != null) { + wagon.addTransferListener(downloadMonitor); + } + } + + // unset the firstRun flag, so we don't get caught in an infinite loop... + firstRun = false; + } + } catch (ConnectionException e) { + throw new TransferFailedException("Connection failed: " + e.getMessage(), e); + } catch (AuthenticationException e) { + throw new TransferFailedException("Authentication failed: " + e.getMessage(), e); + } catch (AuthorizationException e) { + throw new TransferFailedException("Authorization failed: " + e.getMessage(), e); + } finally { + // Remove remaining TransferListener instances (checksum handlers removed in above finally clause) + if (downloadMonitor != null) { + wagon.removeTransferListener(downloadMonitor); + } + + disconnectWagon(wagon); + + releaseWagon(protocol, wagon); + } + + if (downloaded) { + if (!temp.exists()) { + throw new ResourceDoesNotExistException("Downloaded file does not exist: " + temp); + } + + // The temporary file is named destination + ".tmp" and is done this way to ensure + // that the temporary file is in the same file system as the destination because the + // File.renameTo operation doesn't really work across file systems. + // So we will attempt to do a File.renameTo for efficiency and atomicity, if this fails + // then we will use a brute force copy and delete the temporary file. + if (!temp.renameTo(destination)) { + try { + Files.copy( + temp.toPath(), + destination.toPath(), + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES); + + if (!temp.delete()) { + temp.deleteOnExit(); + } + } catch (IOException e) { + throw new TransferFailedException( + "Error copying temporary file to the final destination: " + e.getMessage(), e); + } + } + } + } + + // + // Publisher + // + @Override + public void putArtifact( + File source, Artifact artifact, ArtifactRepository deploymentRepository, TransferListener downloadMonitor) + throws TransferFailedException { + putRemoteFile(deploymentRepository, source, deploymentRepository.pathOf(artifact), downloadMonitor); + } + + @Override + public void putArtifactMetadata(File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository) + throws TransferFailedException { + logger.info("Uploading " + artifactMetadata); + putRemoteFile(repository, source, repository.pathOfRemoteRepositoryMetadata(artifactMetadata), null); + } + + @Override + public void putRemoteFile( + ArtifactRepository repository, File source, String remotePath, TransferListener downloadMonitor) + throws TransferFailedException { + String protocol = repository.getProtocol(); + + Wagon wagon; + try { + wagon = getWagon(protocol); + } catch (UnsupportedProtocolException e) { + throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e); + } + + if (downloadMonitor != null) { + wagon.addTransferListener(downloadMonitor); + } + + Map checksums = new HashMap<>(2); + + Map sums = new HashMap<>(2); + + // TODO configure these on the repository + for (int i = 0; i < CHECKSUM_IDS.length; i++) { + checksums.put(CHECKSUM_IDS[i], addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i])); + } + + List temporaryFiles = new ArrayList<>(); + + try { + try { + connectWagon(wagon, repository); + + wagon.put(source, remotePath); + } finally { + if (downloadMonitor != null) { + wagon.removeTransferListener(downloadMonitor); + } + } + + // Pre-store the checksums as any future puts will overwrite them + for (String extension : checksums.keySet()) { + ChecksumObserver observer = checksums.get(extension); + sums.put(extension, observer.getActualChecksum()); + } + + // We do this in here so we can checksum the artifact metadata too, otherwise it could be metadata itself + for (String extension : checksums.keySet()) { + // TODO shouldn't need a file intermediary - improve wagon to take a stream + File temp = File.createTempFile("maven-artifact", null); + temp.deleteOnExit(); + byte[] bytes = sums.get(extension).getBytes(StandardCharsets.UTF_8); + Files.write( + Paths.get(temp.getAbsolutePath()), bytes, StandardOpenOption.APPEND, StandardOpenOption.CREATE); + + temporaryFiles.add(temp); + wagon.put(temp, remotePath + "." + extension); + } + } catch (ConnectionException e) { + throw new TransferFailedException("Connection failed: " + e.getMessage(), e); + } catch (AuthenticationException e) { + throw new TransferFailedException("Authentication failed: " + e.getMessage(), e); + } catch (AuthorizationException e) { + throw new TransferFailedException("Authorization failed: " + e.getMessage(), e); + } catch (ResourceDoesNotExistException e) { + throw new TransferFailedException("Resource to deploy not found: " + e.getMessage(), e); + } catch (IOException e) { + throw new TransferFailedException("Error creating temporary file for deployment: " + e.getMessage(), e); + } finally { + // MNG-4543 + cleanupTemporaryFiles(temporaryFiles); + + // Remove every checksum listener + for (String id : CHECKSUM_IDS) { + TransferListener checksumListener = checksums.get(id); + if (checksumListener != null) { + wagon.removeTransferListener(checksumListener); + } + } + + disconnectWagon(wagon); + + releaseWagon(protocol, wagon); + } + } + + private void cleanupTemporaryFiles(List files) { + for (File file : files) { + // really don't care if it failed here only log warning + if (!file.delete()) { + logger.warn("skip failed to delete temporary file : " + file.getAbsolutePath()); + file.deleteOnExit(); + } + } + } + + private ChecksumObserver addChecksumObserver(Wagon wagon, String algorithm) throws TransferFailedException { + try { + ChecksumObserver checksumObserver = new ChecksumObserver(algorithm); + wagon.addTransferListener(checksumObserver); + return checksumObserver; + } catch (NoSuchAlgorithmException e) { + throw new TransferFailedException("Unable to add checksum for unsupported algorithm " + algorithm, e); + } + } + + private void handleChecksumFailure(String checksumPolicy, String message, Throwable cause) + throws ChecksumFailedException { + if (ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(checksumPolicy)) { + throw new ChecksumFailedException(message, cause); + } else if (!ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(checksumPolicy)) { + // warn if it is set to anything other than ignore + logger.warn("*** CHECKSUM FAILED - " + message + " - IGNORING"); + } + // otherwise it is ignored + } + + private void verifyChecksum( + ChecksumObserver checksumObserver, + File destination, + File tempDestination, + String remotePath, + String checksumFileExtension, + Wagon wagon) + throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException { + try { + // grab it first, because it's about to change... + String actualChecksum = checksumObserver.getActualChecksum(); + + File tempChecksumFile = new File(tempDestination + checksumFileExtension + ".tmp"); + tempChecksumFile.deleteOnExit(); + wagon.get(remotePath + checksumFileExtension, tempChecksumFile); + byte[] bytes = Files.readAllBytes(tempChecksumFile.toPath()); + String expectedChecksum = new String(bytes, StandardCharsets.UTF_8); + + // remove whitespaces at the end + expectedChecksum = expectedChecksum.trim(); + + // check for 'ALGO (name) = CHECKSUM' like used by openssl + if (expectedChecksum.regionMatches(true, 0, "MD", 0, 2) + || expectedChecksum.regionMatches(true, 0, "SHA", 0, 3)) { + int lastSpacePos = expectedChecksum.lastIndexOf(' '); + expectedChecksum = expectedChecksum.substring(lastSpacePos + 1); + } else { + // remove everything after the first space (if available) + int spacePos = expectedChecksum.indexOf(' '); + + if (spacePos != -1) { + expectedChecksum = expectedChecksum.substring(0, spacePos); + } + } + if (expectedChecksum.equalsIgnoreCase(actualChecksum)) { + File checksumFile = new File(destination + checksumFileExtension); + if (checksumFile.exists()) { + checksumFile.delete(); // ignore if failed as we will overwrite + } + Files.copy( + tempChecksumFile.toPath(), + checksumFile.toPath(), + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES); + + if (!tempChecksumFile.delete()) { + tempChecksumFile.deleteOnExit(); + } + } else { + throw new ChecksumFailedException("Checksum failed on download: local = '" + actualChecksum + + "'; remote = '" + expectedChecksum + "'"); + } + } catch (IOException e) { + throw new ChecksumFailedException("Invalid checksum file", e); + } + } + + private void disconnectWagon(Wagon wagon) { + try { + wagon.disconnect(); + } catch (ConnectionException e) { + logger.error("Problem disconnecting from wagon - ignoring: " + e.getMessage()); + } + } + + private void releaseWagon(String protocol, Wagon wagon) { + try { + container.release(wagon); + } catch (ComponentLifecycleException e) { + logger.error("Problem releasing wagon - ignoring: " + e.getMessage()); + logger.debug("", e); + } + } + + @Override + @Deprecated + public Wagon getWagon(Repository repository) throws UnsupportedProtocolException { + return getWagon(repository.getProtocol()); + } + + @Override + @Deprecated + public Wagon getWagon(String protocol) throws UnsupportedProtocolException { + if (protocol == null) { + throw new UnsupportedProtocolException("Unspecified protocol"); + } + + String hint = protocol.toLowerCase(java.util.Locale.ENGLISH); + + Wagon wagon; + try { + wagon = container.lookup(Wagon.class, hint); + } catch (ComponentLookupException e) { + throw new UnsupportedProtocolException( + "Cannot find wagon which supports the requested protocol: " + protocol, e); + } + + return wagon; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/LegacyRepositorySystem.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/LegacyRepositorySystem.java new file mode 100644 index 000000000000..f748a477d229 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/LegacyRepositorySystem.java @@ -0,0 +1,805 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.Authentication; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Exclusion; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Repository; +import org.apache.maven.model.RepositoryPolicy; +import org.apache.maven.repository.ArtifactDoesNotExistException; +import org.apache.maven.repository.ArtifactTransferFailedException; +import org.apache.maven.repository.ArtifactTransferListener; +import org.apache.maven.repository.DelegatingLocalArtifactRepository; +import org.apache.maven.repository.LocalArtifactRepository; +import org.apache.maven.repository.MirrorSelector; +import org.apache.maven.repository.Proxy; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.repository.legacy.repository.ArtifactRepositoryFactory; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.building.SettingsProblem; +import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecrypter; +import org.apache.maven.settings.crypto.SettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecryptionResult; +import org.apache.maven.wagon.proxy.ProxyInfo; +import org.apache.maven.wagon.proxy.ProxyUtils; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.logging.Logger; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.AuthenticationContext; +import org.eclipse.aether.repository.AuthenticationSelector; +import org.eclipse.aether.repository.ProxySelector; +import org.eclipse.aether.repository.RemoteRepository; + +/** + */ +@Named("default") +@Singleton +@Deprecated +public class LegacyRepositorySystem implements RepositorySystem { + + @Inject + private Logger logger; + + @Inject + private ArtifactFactory artifactFactory; + + @Inject + private ArtifactResolver artifactResolver; + + @Inject + private ArtifactRepositoryFactory artifactRepositoryFactory; + + @Inject + private Map layouts; + + @Inject + private WagonManager wagonManager; + + @Inject + private PlexusContainer plexus; + + @Inject + private MirrorSelector mirrorSelector; + + @Inject + private SettingsDecrypter settingsDecrypter; + + @Override + public Artifact createArtifact(String groupId, String artifactId, String version, String scope, String type) { + return artifactFactory.createArtifact(groupId, artifactId, version, scope, type); + } + + @Override + public Artifact createArtifact(String groupId, String artifactId, String version, String packaging) { + return artifactFactory.createBuildArtifact(groupId, artifactId, version, packaging); + } + + @Override + public Artifact createArtifactWithClassifier( + String groupId, String artifactId, String version, String type, String classifier) { + return artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, type, classifier); + } + + @Override + public Artifact createProjectArtifact(String groupId, String artifactId, String metaVersionId) { + return artifactFactory.createProjectArtifact(groupId, artifactId, metaVersionId); + } + + @Override + public Artifact createDependencyArtifact(Dependency d) { + VersionRange versionRange; + try { + versionRange = VersionRange.createFromVersionSpec(d.getVersion()); + } catch (InvalidVersionSpecificationException e) { + // MNG-5368: Log a message instead of returning 'null' silently. + this.logger.error( + String.format( + "Invalid version specification '%s' creating dependency artifact '%s'.", d.getVersion(), d), + e); + return null; + } + + Artifact artifact = artifactFactory.createDependencyArtifact( + d.getGroupId(), + d.getArtifactId(), + versionRange, + d.getType(), + d.getClassifier(), + d.getScope(), + d.isOptional()); + + if (Artifact.SCOPE_SYSTEM.equals(d.getScope()) && d.getSystemPath() != null) { + artifact.setFile(new File(d.getSystemPath())); + } + + if (!d.getExclusions().isEmpty()) { + List exclusions = new ArrayList<>(); + + for (Exclusion exclusion : d.getExclusions()) { + exclusions.add(exclusion.getGroupId() + ':' + exclusion.getArtifactId()); + } + + artifact.setDependencyFilter(new ExcludesArtifactFilter(exclusions)); + } + + return artifact; + } + + public Artifact createExtensionArtifact(String groupId, String artifactId, String version) { + VersionRange versionRange; + try { + versionRange = VersionRange.createFromVersionSpec(version); + } catch (InvalidVersionSpecificationException e) { + // MNG-5368: Log a message instead of returning 'null' silently. + this.logger.error( + String.format( + "Invalid version specification '%s' creating extension artifact '%s:%s:%s'.", + version, groupId, artifactId, version), + e); + + return null; + } + + return artifactFactory.createExtensionArtifact(groupId, artifactId, versionRange); + } + + public Artifact createParentArtifact(String groupId, String artifactId, String version) { + return artifactFactory.createParentArtifact(groupId, artifactId, version); + } + + @Override + public Artifact createPluginArtifact(Plugin plugin) { + String version = plugin.getVersion(); + if (version == null || version.isEmpty()) { + version = "RELEASE"; + } + + VersionRange versionRange; + try { + versionRange = VersionRange.createFromVersionSpec(version); + } catch (InvalidVersionSpecificationException e) { + // MNG-5368: Log a message instead of returning 'null' silently. + this.logger.error( + String.format("Invalid version specification '%s' creating plugin artifact '%s'.", version, plugin), + e); + + return null; + } + + return artifactFactory.createPluginArtifact(plugin.getGroupId(), plugin.getArtifactId(), versionRange); + } + + public ArtifactRepositoryPolicy buildArtifactRepositoryPolicy(RepositoryPolicy policy) { + boolean enabled = true; + + String updatePolicy = null; + + String checksumPolicy = null; + + if (policy != null) { + enabled = policy.isEnabled(); + + if (policy.getUpdatePolicy() != null) { + updatePolicy = policy.getUpdatePolicy(); + } + if (policy.getChecksumPolicy() != null) { + checksumPolicy = policy.getChecksumPolicy(); + } + } + + return new ArtifactRepositoryPolicy(enabled, updatePolicy, checksumPolicy); + } + + @Override + public ArtifactRepository createDefaultLocalRepository() throws InvalidRepositoryException { + return createLocalRepository(RepositorySystem.defaultUserLocalRepository); + } + + @Override + public ArtifactRepository createLocalRepository(File localRepository) throws InvalidRepositoryException { + return createRepository( + "file://" + localRepository.toURI().getRawPath(), + RepositorySystem.DEFAULT_LOCAL_REPO_ID, + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, + ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE); + } + + @Override + public ArtifactRepository createDefaultRemoteRepository() throws InvalidRepositoryException { + return createRepository( + RepositorySystem.DEFAULT_REMOTE_REPO_URL, + RepositorySystem.DEFAULT_REMOTE_REPO_ID, + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_DAILY, + false, + ArtifactRepositoryPolicy.UPDATE_POLICY_DAILY, + ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); + } + + public ArtifactRepository createLocalRepository(String url, String repositoryId) throws IOException { + return createRepository( + canonicalFileUrl(url), + repositoryId, + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, + true, + ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, + ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE); + } + + private String canonicalFileUrl(String url) throws IOException { + if (!url.startsWith("file:")) { + url = "file://" + url; + } else if (url.startsWith("file:") && !url.startsWith("file://")) { + url = "file://" + url.substring("file:".length()); + } + + // So now we have an url of the form file:// + + // We want to eliminate any relative path nonsense and lock down the path so we + // need to fully resolve it before any submodules use the path. This can happen + // when you are using a custom settings.xml that contains a relative path entry + // for the local repository setting. + + File localRepository = new File(url.substring("file://".length())); + + if (!localRepository.isAbsolute()) { + url = "file://" + localRepository.getCanonicalPath(); + } + + return url; + } + + @Override + public ArtifactResolutionResult resolve(ArtifactResolutionRequest request) { + /* + * Probably is not worth it, but here I make sure I restore request + * to its original state. + */ + try { + LocalArtifactRepository ideWorkspace = + plexus.lookup(LocalArtifactRepository.class, LocalArtifactRepository.IDE_WORKSPACE); + + if (request.getLocalRepository() instanceof DelegatingLocalArtifactRepository delegatingLocalRepository) { + LocalArtifactRepository orig = delegatingLocalRepository.getIdeWorkspace(); + + delegatingLocalRepository.setIdeWorkspace(ideWorkspace); + + try { + return artifactResolver.resolve(request); + } finally { + delegatingLocalRepository.setIdeWorkspace(orig); + } + } else { + ArtifactRepository localRepository = request.getLocalRepository(); + DelegatingLocalArtifactRepository delegatingLocalRepository = + new DelegatingLocalArtifactRepository(localRepository); + delegatingLocalRepository.setIdeWorkspace(ideWorkspace); + request.setLocalRepository(delegatingLocalRepository); + try { + return artifactResolver.resolve(request); + } finally { + request.setLocalRepository(localRepository); + } + } + } catch (ComponentLookupException e) { + // no ide workspace artifact resolution + } + + return artifactResolver.resolve(request); + } + + @Override + public List getEffectiveRepositories(List repositories) { + if (repositories == null) { + return null; + } + + Map> reposByKey = new LinkedHashMap<>(); + + for (ArtifactRepository repository : repositories) { + String key = repository.getId(); + + List aliasedRepos = reposByKey.computeIfAbsent(key, k -> new ArrayList<>()); + + aliasedRepos.add(repository); + } + + List effectiveRepositories = new ArrayList<>(); + + for (List aliasedRepos : reposByKey.values()) { + List mirroredRepos = new ArrayList<>(); + + List releasePolicies = new ArrayList<>(aliasedRepos.size()); + + for (ArtifactRepository aliasedRepo : aliasedRepos) { + releasePolicies.add(aliasedRepo.getReleases()); + mirroredRepos.addAll(aliasedRepo.getMirroredRepositories()); + } + + ArtifactRepositoryPolicy releasePolicy = getEffectivePolicy(releasePolicies); + + List snapshotPolicies = new ArrayList<>(aliasedRepos.size()); + + for (ArtifactRepository aliasedRepo : aliasedRepos) { + snapshotPolicies.add(aliasedRepo.getSnapshots()); + } + + ArtifactRepositoryPolicy snapshotPolicy = getEffectivePolicy(snapshotPolicies); + + ArtifactRepository aliasedRepo = aliasedRepos.get(0); + + ArtifactRepository effectiveRepository = createArtifactRepository( + aliasedRepo.getId(), aliasedRepo.getUrl(), aliasedRepo.getLayout(), snapshotPolicy, releasePolicy); + + effectiveRepository.setAuthentication(aliasedRepo.getAuthentication()); + + effectiveRepository.setProxy(aliasedRepo.getProxy()); + + effectiveRepository.setMirroredRepositories(mirroredRepos); + + effectiveRepository.setBlocked(aliasedRepo.isBlocked()); + + effectiveRepositories.add(effectiveRepository); + } + + return effectiveRepositories; + } + + private ArtifactRepositoryPolicy getEffectivePolicy(Collection policies) { + ArtifactRepositoryPolicy effectivePolicy = null; + + for (ArtifactRepositoryPolicy policy : policies) { + if (effectivePolicy == null) { + effectivePolicy = new ArtifactRepositoryPolicy(policy); + } else { + effectivePolicy.merge(policy); + } + } + + return effectivePolicy; + } + + @Override + public Mirror getMirror(ArtifactRepository repository, List mirrors) { + return mirrorSelector.getMirror(repository, mirrors); + } + + @Override + public void injectMirror(List repositories, List mirrors) { + if (repositories != null && mirrors != null) { + for (ArtifactRepository repository : repositories) { + Mirror mirror = getMirror(repository, mirrors); + injectMirror(repository, mirror); + } + } + } + + private Mirror getMirror(RepositorySystemSession session, ArtifactRepository repository) { + if (session != null) { + org.eclipse.aether.repository.MirrorSelector selector = session.getMirrorSelector(); + if (selector != null) { + RemoteRepository repo = selector.getMirror(RepositoryUtils.toRepo(repository)); + if (repo != null) { + Mirror mirror = new Mirror(); + mirror.setId(repo.getId()); + mirror.setUrl(repo.getUrl()); + mirror.setLayout(repo.getContentType()); + mirror.setBlocked(repo.isBlocked()); + return mirror; + } + } + } + return null; + } + + @Override + public void injectMirror(RepositorySystemSession session, List repositories) { + if (repositories != null && session != null) { + for (ArtifactRepository repository : repositories) { + Mirror mirror = getMirror(session, repository); + injectMirror(repository, mirror); + } + } + } + + private void injectMirror(ArtifactRepository repository, Mirror mirror) { + if (mirror != null) { + ArtifactRepository original = createArtifactRepository( + repository.getId(), + repository.getUrl(), + repository.getLayout(), + repository.getSnapshots(), + repository.getReleases()); + + repository.setMirroredRepositories(Collections.singletonList(original)); + + repository.setId(mirror.getId()); + repository.setUrl(mirror.getUrl()); + + if (mirror.getLayout() != null && !mirror.getLayout().isEmpty()) { + repository.setLayout(getLayout(mirror.getLayout())); + } + + repository.setBlocked(mirror.isBlocked()); + } + } + + @Override + public void injectAuthentication(List repositories, List servers) { + if (repositories != null) { + Map serversById = new HashMap<>(); + + if (servers != null) { + for (Server server : servers) { + if (!serversById.containsKey(server.getId())) { + serversById.put(server.getId(), server); + } + } + } + + for (ArtifactRepository repository : repositories) { + Server server = serversById.get(repository.getId()); + + if (server != null) { + SettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(server); + SettingsDecryptionResult result = settingsDecrypter.decrypt(request); + server = result.getServer(); + + if (logger.isDebugEnabled()) { + for (SettingsProblem problem : result.getProblems()) { + logger.debug(problem.getMessage(), problem.getException()); + } + } + + Authentication authentication = new Authentication(server.getUsername(), server.getPassword()); + authentication.setPrivateKey(server.getPrivateKey()); + authentication.setPassphrase(server.getPassphrase()); + + repository.setAuthentication(authentication); + } else { + repository.setAuthentication(null); + } + } + } + } + + private Authentication getAuthentication(RepositorySystemSession session, ArtifactRepository repository) { + if (session != null) { + AuthenticationSelector selector = session.getAuthenticationSelector(); + if (selector != null) { + RemoteRepository repo = RepositoryUtils.toRepo(repository); + org.eclipse.aether.repository.Authentication auth = selector.getAuthentication(repo); + if (auth != null) { + repo = new RemoteRepository.Builder(repo) + .setAuthentication(auth) + .build(); + AuthenticationContext authCtx = AuthenticationContext.forRepository(session, repo); + Authentication result = new Authentication( + authCtx.get(AuthenticationContext.USERNAME), authCtx.get(AuthenticationContext.PASSWORD)); + result.setPrivateKey(authCtx.get(AuthenticationContext.PRIVATE_KEY_PATH)); + result.setPassphrase(authCtx.get(AuthenticationContext.PRIVATE_KEY_PASSPHRASE)); + authCtx.close(); + return result; + } + } + } + return null; + } + + @Override + public void injectAuthentication(RepositorySystemSession session, List repositories) { + if (repositories != null && session != null) { + for (ArtifactRepository repository : repositories) { + repository.setAuthentication(getAuthentication(session, repository)); + } + } + } + + private org.apache.maven.settings.Proxy getProxy( + ArtifactRepository repository, List proxies) { + if (proxies != null && repository.getProtocol() != null) { + for (org.apache.maven.settings.Proxy proxy : proxies) { + if (proxy.isActive() && repository.getProtocol().equalsIgnoreCase(proxy.getProtocol())) { + if (proxy.getNonProxyHosts() != null + && !proxy.getNonProxyHosts().isEmpty()) { + ProxyInfo pi = new ProxyInfo(); + pi.setNonProxyHosts(proxy.getNonProxyHosts()); + + org.apache.maven.wagon.repository.Repository repo = + new org.apache.maven.wagon.repository.Repository( + repository.getId(), repository.getUrl()); + + if (!ProxyUtils.validateNonProxyHosts(pi, repo.getHost())) { + return proxy; + } + } else { + return proxy; + } + } + } + } + + return null; + } + + @Override + public void injectProxy(List repositories, List proxies) { + if (repositories != null) { + for (ArtifactRepository repository : repositories) { + org.apache.maven.settings.Proxy proxy = getProxy(repository, proxies); + + if (proxy != null) { + SettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(proxy); + SettingsDecryptionResult result = settingsDecrypter.decrypt(request); + proxy = result.getProxy(); + + if (logger.isDebugEnabled()) { + for (SettingsProblem problem : result.getProblems()) { + logger.debug(problem.getMessage(), problem.getException()); + } + } + + Proxy p = new Proxy(); + p.setHost(proxy.getHost()); + p.setProtocol(proxy.getProtocol()); + p.setPort(proxy.getPort()); + p.setNonProxyHosts(proxy.getNonProxyHosts()); + p.setUserName(proxy.getUsername()); + p.setPassword(proxy.getPassword()); + + repository.setProxy(p); + } else { + repository.setProxy(null); + } + } + } + } + + private Proxy getProxy(RepositorySystemSession session, ArtifactRepository repository) { + if (session != null) { + ProxySelector selector = session.getProxySelector(); + if (selector != null) { + RemoteRepository repo = RepositoryUtils.toRepo(repository); + org.eclipse.aether.repository.Proxy proxy = selector.getProxy(repo); + if (proxy != null) { + Proxy p = new Proxy(); + p.setHost(proxy.getHost()); + p.setProtocol(proxy.getType()); + p.setPort(proxy.getPort()); + if (proxy.getAuthentication() != null) { + repo = new RemoteRepository.Builder(repo) + .setProxy(proxy) + .build(); + AuthenticationContext authCtx = AuthenticationContext.forProxy(session, repo); + p.setUserName(authCtx.get(AuthenticationContext.USERNAME)); + p.setPassword(authCtx.get(AuthenticationContext.PASSWORD)); + p.setNtlmDomain(authCtx.get(AuthenticationContext.NTLM_DOMAIN)); + p.setNtlmHost(authCtx.get(AuthenticationContext.NTLM_WORKSTATION)); + authCtx.close(); + } + return p; + } + } + } + return null; + } + + @Override + public void injectProxy(RepositorySystemSession session, List repositories) { + if (repositories != null && session != null) { + for (ArtifactRepository repository : repositories) { + repository.setProxy(getProxy(session, repository)); + } + } + } + + @Override + public void retrieve( + ArtifactRepository repository, + File destination, + String remotePath, + ArtifactTransferListener transferListener) + throws ArtifactTransferFailedException, ArtifactDoesNotExistException { + try { + wagonManager.getRemoteFile( + repository, + destination, + remotePath, + TransferListenerAdapter.newAdapter(transferListener), + ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN, + true); + } catch (org.apache.maven.wagon.TransferFailedException e) { + throw new ArtifactTransferFailedException(getMessage(e, "Error transferring artifact."), e); + } catch (org.apache.maven.wagon.ResourceDoesNotExistException e) { + throw new ArtifactDoesNotExistException(getMessage(e, "Requested artifact does not exist."), e); + } + } + + @Override + public void publish( + ArtifactRepository repository, File source, String remotePath, ArtifactTransferListener transferListener) + throws ArtifactTransferFailedException { + try { + wagonManager.putRemoteFile( + repository, source, remotePath, TransferListenerAdapter.newAdapter(transferListener)); + } catch (org.apache.maven.wagon.TransferFailedException e) { + throw new ArtifactTransferFailedException(getMessage(e, "Error transferring artifact."), e); + } + } + + // + // Artifact Repository Creation + // + @Override + public ArtifactRepository buildArtifactRepository(Repository repo) throws InvalidRepositoryException { + if (repo != null) { + String id = repo.getId(); + + if (id == null || id.isEmpty()) { + throw new InvalidRepositoryException("Repository identifier missing", ""); + } + + String url = repo.getUrl(); + + if (url == null || url.isEmpty()) { + throw new InvalidRepositoryException("URL missing for repository " + id, id); + } + + ArtifactRepositoryPolicy snapshots = buildArtifactRepositoryPolicy(repo.getSnapshots()); + + ArtifactRepositoryPolicy releases = buildArtifactRepositoryPolicy(repo.getReleases()); + + return createArtifactRepository(id, url, getLayout(repo.getLayout()), snapshots, releases); + } else { + return null; + } + } + + private ArtifactRepository createRepository( + String url, + String repositoryId, + boolean releases, + String releaseUpdates, + boolean snapshots, + String snapshotUpdates, + String checksumPolicy) { + ArtifactRepositoryPolicy snapshotsPolicy = + new ArtifactRepositoryPolicy(snapshots, snapshotUpdates, checksumPolicy); + + ArtifactRepositoryPolicy releasesPolicy = + new ArtifactRepositoryPolicy(releases, releaseUpdates, checksumPolicy); + + return createArtifactRepository(repositoryId, url, null, snapshotsPolicy, releasesPolicy); + } + + @Override + public ArtifactRepository createArtifactRepository( + String repositoryId, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) { + if (repositoryLayout == null) { + repositoryLayout = layouts.get("default"); + } + return artifactRepositoryFactory.createArtifactRepository( + repositoryId, url, repositoryLayout, snapshots, releases); + } + + private static String getMessage(Throwable error, String def) { + if (error == null) { + return def; + } + String msg = error.getMessage(); + if (msg != null && !msg.isEmpty()) { + return msg; + } + return getMessage(error.getCause(), def); + } + + private ArtifactRepositoryLayout getLayout(String id) { + ArtifactRepositoryLayout layout = layouts.get(id); + + if (layout == null) { + layout = new UnknownRepositoryLayout(id, layouts.get("default")); + } + + return layout; + } + + /** + * In the future, the legacy system might encounter repository types for which no layout components exists because + * the actual communication with the repository happens via a repository connector. As a minimum, the legacy system + * needs to retain the id of this layout so that the content type of the remote repository can still be accurately + * described. + */ + static class UnknownRepositoryLayout implements ArtifactRepositoryLayout { + + private final String id; + + private final ArtifactRepositoryLayout fallback; + + UnknownRepositoryLayout(String id, ArtifactRepositoryLayout fallback) { + this.id = id; + this.fallback = fallback; + } + + @Override + public String getId() { + return id; + } + + @Override + public String pathOf(Artifact artifact) { + return fallback.pathOf(artifact); + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return fallback.pathOfLocalRepositoryMetadata(metadata, repository); + } + + @Override + public String pathOfRemoteRepositoryMetadata(ArtifactMetadata metadata) { + return fallback.pathOfRemoteRepositoryMetadata(metadata); + } + + @Override + public String toString() { + return getId(); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/MavenArtifact.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/MavenArtifact.java new file mode 100644 index 000000000000..fbf7a12b77fa --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/MavenArtifact.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import org.apache.maven.repository.ArtifactTransferResource; +import org.apache.maven.wagon.resource.Resource; + +@Deprecated +class MavenArtifact implements ArtifactTransferResource { + + private String repositoryUrl; + + private Resource resource; + + private long transferStartTime; + + MavenArtifact(String repositoryUrl, Resource resource) { + if (repositoryUrl == null) { + this.repositoryUrl = ""; + } else if (!repositoryUrl.endsWith("/") && !repositoryUrl.isEmpty()) { + this.repositoryUrl = repositoryUrl + '/'; + } else { + this.repositoryUrl = repositoryUrl; + } + this.resource = resource; + + this.transferStartTime = System.currentTimeMillis(); + } + + @Override + public String getRepositoryUrl() { + return repositoryUrl; + } + + @Override + public String getName() { + String name = resource.getName(); + + if (name == null) { + name = ""; + } else if (name.startsWith("/")) { + name = name.substring(1); + } + + return name; + } + + @Override + public String getUrl() { + return getRepositoryUrl() + getName(); + } + + @Override + public long getContentLength() { + return resource.getContentLength(); + } + + @Override + public long getTransferStartTime() { + return transferStartTime; + } + + @Override + public String toString() { + return getUrl(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/TransferListenerAdapter.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/TransferListenerAdapter.java new file mode 100644 index 000000000000..01398b054871 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/TransferListenerAdapter.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import java.util.IdentityHashMap; +import java.util.Map; + +import org.apache.maven.repository.ArtifactTransferEvent; +import org.apache.maven.repository.ArtifactTransferListener; +import org.apache.maven.repository.ArtifactTransferResource; +import org.apache.maven.wagon.events.TransferEvent; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.repository.Repository; +import org.apache.maven.wagon.resource.Resource; + +/** + * TransferListenerAdapter + */ +@Deprecated +public class TransferListenerAdapter implements TransferListener { + + private final ArtifactTransferListener listener; + + private final Map artifacts; + + private final Map transfers; + + public static TransferListener newAdapter(ArtifactTransferListener listener) { + if (listener == null) { + return null; + } else { + return new TransferListenerAdapter(listener); + } + } + + private TransferListenerAdapter(ArtifactTransferListener listener) { + this.listener = listener; + this.artifacts = new IdentityHashMap<>(); + this.transfers = new IdentityHashMap<>(); + } + + @Override + public void debug(String message) {} + + @Override + public void transferCompleted(TransferEvent transferEvent) { + ArtifactTransferEvent event = wrap(transferEvent); + + Long transferred; + synchronized (transfers) { + transferred = transfers.remove(transferEvent.getResource()); + } + if (transferred != null) { + event.setTransferredBytes(transferred); + } + + synchronized (artifacts) { + artifacts.remove(transferEvent.getResource()); + } + + listener.transferCompleted(event); + } + + @Override + public void transferError(TransferEvent transferEvent) { + synchronized (transfers) { + transfers.remove(transferEvent.getResource()); + } + synchronized (artifacts) { + artifacts.remove(transferEvent.getResource()); + } + } + + @Override + public void transferInitiated(TransferEvent transferEvent) { + listener.transferInitiated(wrap(transferEvent)); + } + + @Override + public void transferProgress(TransferEvent transferEvent, byte[] buffer, int length) { + Long transferred; + synchronized (transfers) { + transferred = transfers.get(transferEvent.getResource()); + if (transferred == null) { + transferred = (long) length; + } else { + transferred = transferred + length; + } + transfers.put(transferEvent.getResource(), transferred); + } + + ArtifactTransferEvent event = wrap(transferEvent); + event.setDataBuffer(buffer); + event.setDataOffset(0); + event.setDataLength(length); + event.setTransferredBytes(transferred); + + listener.transferProgress(event); + } + + @Override + public void transferStarted(TransferEvent transferEvent) { + listener.transferStarted(wrap(transferEvent)); + } + + private ArtifactTransferEvent wrap(TransferEvent event) { + if (event == null) { + return null; + } else { + String wagon = event.getWagon().getClass().getName(); + + ArtifactTransferResource artifact = wrap(event.getWagon().getRepository(), event.getResource()); + + ArtifactTransferEvent evt; + if (event.getException() != null) { + evt = new ArtifactTransferEvent(wagon, event.getException(), event.getRequestType(), artifact); + } else { + evt = new ArtifactTransferEvent(wagon, event.getEventType(), event.getRequestType(), artifact); + } + + evt.setLocalFile(event.getLocalFile()); + + return evt; + } + } + + private ArtifactTransferResource wrap(Repository repository, Resource resource) { + if (resource == null) { + return null; + } else { + synchronized (artifacts) { + ArtifactTransferResource artifact = artifacts.get(resource); + + if (artifact == null) { + artifact = new MavenArtifact(repository.getUrl(), resource); + artifacts.put(resource, artifact); + } + + return artifact; + } + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/UpdateCheckManager.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/UpdateCheckManager.java new file mode 100644 index 000000000000..957594f57c1a --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/UpdateCheckManager.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import java.io.File; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; + +/** + * UpdateCheckManager + */ +@Deprecated +public interface UpdateCheckManager { + + boolean isUpdateRequired(Artifact artifact, ArtifactRepository repository); + + void touch(Artifact artifact, ArtifactRepository repository, String error); + + String getError(Artifact artifact, ArtifactRepository repository); + + boolean isUpdateRequired(RepositoryMetadata metadata, ArtifactRepository repository, File file); + + void touch(RepositoryMetadata metadata, ArtifactRepository repository, File file); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/WagonConfigurationException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/WagonConfigurationException.java new file mode 100644 index 000000000000..1ebe551c8b96 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/WagonConfigurationException.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import org.apache.maven.wagon.TransferFailedException; + +/** + * WagonConfigurationException + */ +@Deprecated +public class WagonConfigurationException extends TransferFailedException { + + static final long serialVersionUID = 1; + + private final String originalMessage; + private final String repositoryId; + + public WagonConfigurationException(String repositoryId, String message, Throwable cause) { + super("While configuring wagon for '" + repositoryId + "': " + message, cause); + + this.repositoryId = repositoryId; + this.originalMessage = message; + } + + public WagonConfigurationException(String repositoryId, String message) { + super("While configuring wagon for '" + repositoryId + "': " + message); + + this.repositoryId = repositoryId; + this.originalMessage = message; + } + + public final String getRepositoryId() { + return repositoryId; + } + + public final String getOriginalMessage() { + return originalMessage; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/WagonManager.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/WagonManager.java new file mode 100644 index 000000000000..697e86af6e21 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/WagonManager.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import java.io.File; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.UnsupportedProtocolException; +import org.apache.maven.wagon.Wagon; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.repository.Repository; + +/** + * WagonManager + */ +@Deprecated +public interface WagonManager { + @Deprecated + Wagon getWagon(String protocol) throws UnsupportedProtocolException; + + @Deprecated + Wagon getWagon(Repository repository) throws UnsupportedProtocolException, WagonConfigurationException; + + // + // Retriever + // + void getArtifact(Artifact artifact, ArtifactRepository repository, TransferListener transferListener, boolean force) + throws TransferFailedException, ResourceDoesNotExistException; + + void getArtifact( + Artifact artifact, + List remoteRepositories, + TransferListener transferListener, + boolean force) + throws TransferFailedException, ResourceDoesNotExistException; + + void getRemoteFile( + ArtifactRepository repository, + File destination, + String remotePath, + TransferListener downloadMonitor, + String checksumPolicy, + boolean force) + throws TransferFailedException, ResourceDoesNotExistException; + + void getArtifactMetadata( + ArtifactMetadata metadata, ArtifactRepository remoteRepository, File destination, String checksumPolicy) + throws TransferFailedException, ResourceDoesNotExistException; + + void getArtifactMetadataFromDeploymentRepository( + ArtifactMetadata metadata, ArtifactRepository remoteRepository, File file, String checksumPolicyWarn) + throws TransferFailedException, ResourceDoesNotExistException; + + // + // Deployer + // + void putArtifact( + File source, Artifact artifact, ArtifactRepository deploymentRepository, TransferListener downloadMonitor) + throws TransferFailedException; + + void putRemoteFile(ArtifactRepository repository, File source, String remotePath, TransferListener downloadMonitor) + throws TransferFailedException; + + void putArtifactMetadata(File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository) + throws TransferFailedException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/AbstractArtifactMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/AbstractArtifactMetadata.java new file mode 100644 index 000000000000..c72b8389f0b5 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/AbstractArtifactMetadata.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import org.apache.maven.artifact.Artifact; + +/** + * Common elements of artifact metadata. + * + */ +@Deprecated +public abstract class AbstractArtifactMetadata implements ArtifactMetadata { + private static final String LS = System.lineSeparator(); + + protected Artifact artifact; + + protected AbstractArtifactMetadata(Artifact artifact) { + this.artifact = artifact; + } + + @Override + public boolean storedInGroupDirectory() { + return false; + } + + @Override + public String getGroupId() { + return artifact.getGroupId(); + } + + @Override + public String getArtifactId() { + return artifact.getArtifactId(); + } + + @Override + public String extendedToString() { + StringBuilder buffer = new StringBuilder(256); + + buffer.append(LS).append("Artifact Metadata").append(LS).append("--------------------------"); + buffer.append(LS).append("GroupId: ").append(getGroupId()); + buffer.append(LS).append("ArtifactId: ").append(getArtifactId()); + buffer.append(LS).append("Metadata Type: ").append(getClass().getName()); + + return buffer.toString(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadataRetrievalException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadataRetrievalException.java new file mode 100644 index 000000000000..a065eeda7709 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadataRetrievalException.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import org.apache.maven.artifact.Artifact; + +/** + * Error while retrieving repository metadata from the repository. + * + */ +@Deprecated +public class ArtifactMetadataRetrievalException extends Exception { + private Artifact artifact; + + /** + * @param message a message + * @deprecated use {@link #ArtifactMetadataRetrievalException(String, Throwable, Artifact)} + */ + @Deprecated + public ArtifactMetadataRetrievalException(String message) { + this(message, null, null); + } + + /** + * @param cause a cause + * @deprecated use {@link #ArtifactMetadataRetrievalException(String, Throwable, Artifact)} + */ + @Deprecated + public ArtifactMetadataRetrievalException(Throwable cause) { + this(null, cause, null); + } + + /** + * @param message a message + * @param cause a cause + * @deprecated use {@link #ArtifactMetadataRetrievalException(String, Throwable, Artifact)} + */ + @Deprecated + public ArtifactMetadataRetrievalException(String message, Throwable cause) { + this(message, cause, null); + } + + public ArtifactMetadataRetrievalException(String message, Throwable cause, Artifact artifact) { + super(message, cause); + this.artifact = artifact; + } + + public Artifact getArtifact() { + return artifact; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadataSource.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadataSource.java new file mode 100644 index 000000000000..45094119e8ec --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ArtifactMetadataSource.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; + +/** + * Provides some metadata operations, like querying the remote repository for a list of versions available for an + * artifact. + * + */ +@Deprecated +public interface ArtifactMetadataSource { + + ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException; + + ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException; + + /** + * Get a list of available versions for an artifact in the remote repository + * + * @param artifact artifact we are interested in. Only groupid and artifactId + * are needed, for instance the following code will work + * artifactFactory.createProjectArtifact( "org.apache.maven", "maven", "" ) + * @param localRepository local repository + * @param remoteRepositories remote repositories, {@link List} $lt; {@link ArtifactRepository} > + * @return {@link List} $lt; {@link ArtifactVersion} > + * @throws ArtifactMetadataRetrievalException + * in case of error while retrieving repository metadata from the repository. + */ + List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException; + + /** + * Get a list of available versions for an artifact in the remote deployment repository. This ignores any update + * policy checks and mirrors and always retrieves the latest information from the given repository. + * + * @param artifact artifact we are interested in. Only groupid and artifactId are + * needed, for instance the following code will work + * artifactFactory.createProjectArtifact( "org.apache.maven", "maven", "" ) + * @param localRepository local repository + * @param remoteRepository remote repository + * @return {@link List} $lt; {@link ArtifactVersion} > + * @throws ArtifactMetadataRetrievalException + * in case of error while retrieving repository metadata from the repository. + */ + List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws ArtifactMetadataRetrievalException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/DefaultMetadataResolutionRequest.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/DefaultMetadataResolutionRequest.java new file mode 100644 index 000000000000..c8ebf35778d1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/DefaultMetadataResolutionRequest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultRepositoryRequest; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; + +/** + * Forms a request to retrieve artifact metadata. + * + */ +@Deprecated +public class DefaultMetadataResolutionRequest implements MetadataResolutionRequest { + + private Artifact artifact; + + private boolean resolveManagedVersions; + + private RepositoryRequest repositoryRequest; + + public DefaultMetadataResolutionRequest() { + repositoryRequest = new DefaultRepositoryRequest(); + } + + public DefaultMetadataResolutionRequest(RepositoryRequest repositoryRequest) { + this.repositoryRequest = new DefaultRepositoryRequest(repositoryRequest); + } + + public DefaultMetadataResolutionRequest(ArtifactResolutionRequest resolutionRequest) { + this.repositoryRequest = new DefaultRepositoryRequest(resolutionRequest); + } + + @Override + public Artifact getArtifact() { + return artifact; + } + + @Override + public DefaultMetadataResolutionRequest setArtifact(Artifact artifact) { + this.artifact = artifact; + + return this; + } + + @Override + public ArtifactRepository getLocalRepository() { + return repositoryRequest.getLocalRepository(); + } + + @Override + public DefaultMetadataResolutionRequest setLocalRepository(ArtifactRepository localRepository) { + repositoryRequest.setLocalRepository(localRepository); + + return this; + } + + @Override + public List getRemoteRepositories() { + return repositoryRequest.getRemoteRepositories(); + } + + @Override + public DefaultMetadataResolutionRequest setRemoteRepositories(List remoteRepositories) { + repositoryRequest.setRemoteRepositories(remoteRepositories); + + return this; + } + + @Override + public boolean isResolveManagedVersions() { + return resolveManagedVersions; + } + + @Override + public DefaultMetadataResolutionRequest setResolveManagedVersions(boolean resolveManagedVersions) { + this.resolveManagedVersions = resolveManagedVersions; + + return this; + } + + @Override + public boolean isOffline() { + return repositoryRequest.isOffline(); + } + + @Override + public DefaultMetadataResolutionRequest setOffline(boolean offline) { + repositoryRequest.setOffline(offline); + + return this; + } + + @Override + public boolean isForceUpdate() { + return repositoryRequest.isForceUpdate(); + } + + @Override + public DefaultMetadataResolutionRequest setForceUpdate(boolean forceUpdate) { + repositoryRequest.setForceUpdate(forceUpdate); + + return this; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/MetadataResolutionRequest.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/MetadataResolutionRequest.java new file mode 100644 index 000000000000..47e7717f4c96 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/MetadataResolutionRequest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; + +/** + * Forms a request to retrieve artifact metadata. + * + */ +@Deprecated +public interface MetadataResolutionRequest extends RepositoryRequest { + + /** + * Indicates whether network access to remote repositories has been disabled. + * + * @return {@code true} if remote access has been disabled, {@code false} otherwise. + */ + @Override + boolean isOffline(); + + /** + * Enables/disables network access to remote repositories. + * + * @param offline {@code true} to disable remote access, {@code false} to allow network access. + * @return This request, never {@code null}. + */ + @Override + MetadataResolutionRequest setOffline(boolean offline); + + /** + * Gets the artifact to resolve metadata for. + * + * @return The artifact to resolve metadata for or {@code null} if not set. + */ + Artifact getArtifact(); + + /** + * Sets the artifact for which to resolve metadata. + * + * @param artifact The artifact for which to resolve metadata. + * @return This request, never {@code null}. + */ + MetadataResolutionRequest setArtifact(Artifact artifact); + + /** + * Gets the local repository to use for the resolution. + * + * @return The local repository to use for the resolution or {@code null} if not set. + */ + @Override + ArtifactRepository getLocalRepository(); + + /** + * Sets the local repository to use for the resolution. + * + * @param localRepository The local repository to use for the resolution. + * @return This request, never {@code null}. + */ + @Override + MetadataResolutionRequest setLocalRepository(ArtifactRepository localRepository); + + /** + * Gets the remote repositories to use for the resolution. + * + * @return The remote repositories to use for the resolution, never {@code null}. + */ + @Override + List getRemoteRepositories(); + + /** + * Sets the remote repositories to use for the resolution. + * + * @param remoteRepositories The remote repositories to use for the resolution. + * @return This request, never {@code null}. + */ + @Override + MetadataResolutionRequest setRemoteRepositories(List remoteRepositories); + + /** + * Determines whether the managed version information should be retrieved. + * + * @return {@code true} if the dependency management information should be retrieved, {@code false} otherwise. + */ + boolean isResolveManagedVersions(); + + /** + * Enables/disables resolution of the dependency management information. + * + * @param resolveManagedVersions {@code true} if the dependency management information should be retrieved, {@code + * false} otherwise. + * @return This request, never {@code null}. + */ + MetadataResolutionRequest setResolveManagedVersions(boolean resolveManagedVersions); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ResolutionGroup.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ResolutionGroup.java new file mode 100644 index 000000000000..1bb1c9c735c4 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/metadata/ResolutionGroup.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.metadata; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * ResolutionGroup + */ +@Deprecated +public class ResolutionGroup { + + private final Set artifacts; + + private final List resolutionRepositories; + + private final Artifact pomArtifact; + + private final Artifact relocatedArtifact; + + private final Map managedVersions; + + public ResolutionGroup( + Artifact pomArtifact, Set artifacts, List resolutionRepositories) { + this(pomArtifact, null, artifacts, null, resolutionRepositories); + } + + public ResolutionGroup( + Artifact pomArtifact, + Artifact relocatedArtifact, + Set artifacts, + Map managedVersions, + List resolutionRepositories) { + this.pomArtifact = pomArtifact; + this.relocatedArtifact = relocatedArtifact; + this.artifacts = artifacts; + this.managedVersions = managedVersions; + this.resolutionRepositories = resolutionRepositories; + } + + public Artifact getPomArtifact() { + return pomArtifact; + } + + public Artifact getRelocatedArtifact() { + return relocatedArtifact; + } + + public Set getArtifacts() { + return artifacts; + } + + public List getResolutionRepositories() { + return resolutionRepositories; + } + + public Map getManagedVersions() { + return managedVersions; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/repository/ArtifactRepositoryFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/repository/ArtifactRepositoryFactory.java new file mode 100644 index 000000000000..2abe5ea84b8b --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/repository/ArtifactRepositoryFactory.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.repository; + +import org.apache.maven.artifact.UnknownRepositoryLayoutException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; + +@Deprecated +public interface ArtifactRepositoryFactory { + + String DEFAULT_LAYOUT_ID = "default"; + + String LOCAL_REPOSITORY_ID = "local"; + + @Deprecated + ArtifactRepositoryLayout getLayout(String layoutId) throws UnknownRepositoryLayoutException; + + @Deprecated + ArtifactRepository createDeploymentArtifactRepository(String id, String url, String layoutId, boolean uniqueVersion) + throws UnknownRepositoryLayoutException; + + ArtifactRepository createDeploymentArtifactRepository( + String id, String url, ArtifactRepositoryLayout layout, boolean uniqueVersion); + + ArtifactRepository createArtifactRepository( + String id, + String url, + String layoutId, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) + throws UnknownRepositoryLayoutException; + + ArtifactRepository createArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases); + + void setGlobalUpdatePolicy(String snapshotPolicy); + + void setGlobalChecksumPolicy(String checksumPolicy); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/repository/DefaultArtifactRepositoryFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/repository/DefaultArtifactRepositoryFactory.java new file mode 100644 index 000000000000..91df21e9b5d7 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/repository/DefaultArtifactRepositoryFactory.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.repository; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.Map; + +import org.apache.maven.artifact.UnknownRepositoryLayoutException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout2; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultArtifactRepositoryFactory implements ArtifactRepositoryFactory { + // TODO use settings? + private String globalUpdatePolicy; + + private String globalChecksumPolicy; + + @Inject + private Map repositoryLayouts; + + @Override + public ArtifactRepositoryLayout getLayout(String layoutId) throws UnknownRepositoryLayoutException { + return repositoryLayouts.get(layoutId); + } + + @Override + public ArtifactRepository createDeploymentArtifactRepository( + String id, String url, String layoutId, boolean uniqueVersion) throws UnknownRepositoryLayoutException { + ArtifactRepositoryLayout layout = repositoryLayouts.get(layoutId); + + checkLayout(id, layoutId, layout); + + return createDeploymentArtifactRepository(id, url, layout, uniqueVersion); + } + + private void checkLayout(String repositoryId, String layoutId, ArtifactRepositoryLayout layout) + throws UnknownRepositoryLayoutException { + if (layout == null) { + throw new UnknownRepositoryLayoutException(repositoryId, layoutId); + } + } + + @Override + public ArtifactRepository createDeploymentArtifactRepository( + String id, String url, ArtifactRepositoryLayout repositoryLayout, boolean uniqueVersion) { + return createArtifactRepository(id, url, repositoryLayout, null, null); + } + + @Override + public ArtifactRepository createArtifactRepository( + String id, + String url, + String layoutId, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) + throws UnknownRepositoryLayoutException { + ArtifactRepositoryLayout layout = repositoryLayouts.get(layoutId); + + checkLayout(id, layoutId, layout); + + return createArtifactRepository(id, url, layout, snapshots, releases); + } + + @Override + public ArtifactRepository createArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) { + if (snapshots == null) { + snapshots = new ArtifactRepositoryPolicy(); + } + + if (releases == null) { + releases = new ArtifactRepositoryPolicy(); + } + + if (globalUpdatePolicy != null) { + snapshots.setUpdatePolicy(globalUpdatePolicy); + releases.setUpdatePolicy(globalUpdatePolicy); + } + + if (globalChecksumPolicy != null) { + snapshots.setChecksumPolicy(globalChecksumPolicy); + releases.setChecksumPolicy(globalChecksumPolicy); + } + + ArtifactRepository repository; + if (repositoryLayout instanceof ArtifactRepositoryLayout2 artifactRepositoryLayout2) { + repository = artifactRepositoryLayout2.newMavenArtifactRepository(id, url, snapshots, releases); + } else { + repository = new MavenArtifactRepository(id, url, repositoryLayout, snapshots, releases); + } + + return repository; + } + + @Override + public void setGlobalUpdatePolicy(String updatePolicy) { + globalUpdatePolicy = updatePolicy; + } + + @Override + public void setGlobalChecksumPolicy(String checksumPolicy) { + globalChecksumPolicy = checksumPolicy; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/DefaultLegacyArtifactCollector.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/DefaultLegacyArtifactCollector.java new file mode 100644 index 000000000000..f665730fa2c8 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/DefaultLegacyArtifactCollector.java @@ -0,0 +1,753 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.CyclicDependencyException; +import org.apache.maven.artifact.resolver.ResolutionListener; +import org.apache.maven.artifact.resolver.ResolutionListenerForDepMgmt; +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.apache.maven.artifact.resolver.filter.AndArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.ManagedVersionMap; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException; +import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver; +import org.codehaus.plexus.logging.Logger; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultLegacyArtifactCollector implements LegacyArtifactCollector { + + @Inject + @Named("nearest") + private ConflictResolver defaultConflictResolver; + + @Inject + private Logger logger; + + @Inject + private LegacySupport legacySupport; + + private void injectSession(ArtifactResolutionRequest request) { + MavenSession session = legacySupport.getSession(); + + if (session != null) { + request.setOffline(session.isOffline()); + request.setForceUpdate(session.getRequest().isUpdateSnapshots()); + request.setServers(session.getRequest().getServers()); + request.setMirrors(session.getRequest().getMirrors()); + request.setProxies(session.getRequest().getProxies()); + } + } + + @Override + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners, + List conflictResolvers) { + ArtifactResolutionRequest request = new ArtifactResolutionRequest(); + request.setLocalRepository(localRepository); + request.setRemoteRepositories(remoteRepositories); + injectSession(request); + return collect( + artifacts, originatingArtifact, managedVersions, request, source, filter, listeners, conflictResolvers); + } + + @Override + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactResolutionRequest repositoryRequest, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners, + List conflictResolvers) { + ArtifactResolutionResult result = new ArtifactResolutionResult(); + + result.setOriginatingArtifact(originatingArtifact); + + if (conflictResolvers == null) { + conflictResolvers = Collections.singletonList(defaultConflictResolver); + } + + Map> resolvedArtifacts = new LinkedHashMap<>(); + + ResolutionNode root = new ResolutionNode(originatingArtifact, repositoryRequest.getRemoteRepositories()); + + try { + root.addDependencies(artifacts, repositoryRequest.getRemoteRepositories(), filter); + } catch (CyclicDependencyException e) { + result.addCircularDependencyException(e); + + return result; + } catch (OverConstrainedVersionException e) { + result.addVersionRangeViolation(e); + + return result; + } + + ManagedVersionMap versionMap = getManagedVersionsMap(originatingArtifact, managedVersions); + + try { + recurse( + result, + root, + resolvedArtifacts, + versionMap, + repositoryRequest, + source, + filter, + listeners, + conflictResolvers); + } catch (CyclicDependencyException e) { + logger.debug("While recursing: " + e.getMessage(), e); + result.addCircularDependencyException(e); + } catch (OverConstrainedVersionException e) { + logger.debug("While recursing: " + e.getMessage(), e); + result.addVersionRangeViolation(e); + } catch (ArtifactResolutionException e) { + logger.debug("While recursing: " + e.getMessage(), e); + result.addErrorArtifactException(e); + } + + Set set = new LinkedHashSet<>(); + + for (List nodes : resolvedArtifacts.values()) { + for (ResolutionNode node : nodes) { + if (!node.equals(root) && node.isActive()) { + Artifact artifact = node.getArtifact(); + + try { + if (node.filterTrail(filter)) { + // If it was optional and not a direct dependency, + // we don't add it or its children, just allow the update of the version and artifactScope + if (node.isChildOfRootNode() || !artifact.isOptional()) { + artifact.setDependencyTrail(node.getDependencyTrail()); + + set.add(node); + + // This is required right now. + result.addArtifact(artifact); + } + } + } catch (OverConstrainedVersionException e) { + result.addVersionRangeViolation(e); + } + } + } + } + + result.setArtifactResolutionNodes(set); + + return result; + } + + /** + * Get the map of managed versions, removing the originating artifact if it is also in managed versions + * + * @param originatingArtifact artifact we are processing + * @param managedVersions original managed versions + */ + private ManagedVersionMap getManagedVersionsMap( + Artifact originatingArtifact, Map managedVersions) { + ManagedVersionMap versionMap; + if (managedVersions instanceof ManagedVersionMap managedVersionMap) { + versionMap = managedVersionMap; + } else { + versionMap = new ManagedVersionMap(managedVersions); + } + + // remove the originating artifact if it is also in managed versions to avoid being modified during resolution + Artifact managedOriginatingArtifact = versionMap.get(originatingArtifact.getDependencyConflictId()); + + if (managedOriginatingArtifact != null) { + // TODO we probably want to warn the user that he is building an artifact with + // different values than in dependencyManagement + if (managedVersions instanceof ManagedVersionMap) { + /* avoid modifying the managedVersions parameter creating a new map */ + versionMap = new ManagedVersionMap(managedVersions); + } + versionMap.remove(originatingArtifact.getDependencyConflictId()); + } + + return versionMap; + } + + @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"}) + private void recurse( + ArtifactResolutionResult result, + ResolutionNode node, + Map> resolvedArtifacts, + ManagedVersionMap managedVersions, + ArtifactResolutionRequest request, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners, + List conflictResolvers) + throws ArtifactResolutionException { + fireEvent(ResolutionListener.TEST_ARTIFACT, listeners, node); + + Object key = node.getKey(); + + // TODO Does this check need to happen here? Had to add the same call + // below when we iterate on child nodes -- will that suffice? + if (managedVersions.containsKey(key)) { + manageArtifact(node, managedVersions, listeners); + } + + List previousNodes = resolvedArtifacts.get(key); + + if (previousNodes != null) { + for (ResolutionNode previous : previousNodes) { + try { + if (previous.isActive()) { + // Version mediation + VersionRange previousRange = previous.getArtifact().getVersionRange(); + VersionRange currentRange = node.getArtifact().getVersionRange(); + + if ((previousRange != null) && (currentRange != null)) { + // TODO shouldn't need to double up on this work, only done for simplicity of handling + // recommended + // version but the restriction is identical + VersionRange newRange = previousRange.restrict(currentRange); + // TODO ick. this forces the OCE that should have come from the previous call. It is still + // correct + if (newRange.isSelectedVersionKnown(previous.getArtifact())) { + fireEvent( + ResolutionListener.RESTRICT_RANGE, + listeners, + node, + previous.getArtifact(), + newRange); + } + previous.getArtifact().setVersionRange(newRange); + node.getArtifact().setVersionRange(currentRange.restrict(previousRange)); + + // Select an appropriate available version from the (now restricted) range + // Note this version was selected before to get the appropriate POM + // But it was reset by the call to setVersionRange on restricting the version + ResolutionNode[] resetNodes = {previous, node}; + for (int j = 0; j < 2; j++) { + Artifact resetArtifact = resetNodes[j].getArtifact(); + + // MNG-2123: if the previous node was not a range, then it wouldn't have any available + // versions. We just clobbered the selected version above. (why? I have no idea.) + // So since we are here and this is ranges we must go figure out the version (for a + // third time...) + if (resetArtifact.getVersion() == null && resetArtifact.getVersionRange() != null) { + + // go find the version. This is a total hack. See previous comment. + List versions = resetArtifact.getAvailableVersions(); + if (versions == null) { + try { + MetadataResolutionRequest metadataRequest = + new DefaultMetadataResolutionRequest(request); + + metadataRequest.setArtifact(resetArtifact); + versions = source.retrieveAvailableVersions(metadataRequest); + resetArtifact.setAvailableVersions(versions); + } catch (ArtifactMetadataRetrievalException e) { + resetArtifact.setDependencyTrail(node.getDependencyTrail()); + throw new ArtifactResolutionException( + "Unable to get dependency information: " + e.getMessage(), + resetArtifact, + request.getRemoteRepositories(), + e); + } + } + // end hack + + // MNG-2861: match version can return null + ArtifactVersion selectedVersion = resetArtifact + .getVersionRange() + .matchVersion(resetArtifact.getAvailableVersions()); + + if (selectedVersion != null) { + resetArtifact.selectVersion(selectedVersion.toString()); + } else { + throw new OverConstrainedVersionException( + "Unable to find a version in " + resetArtifact.getAvailableVersions() + + " to match the range " + resetArtifact.getVersionRange(), + resetArtifact); + } + + fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, resetNodes[j]); + } + } + } + + // Conflict Resolution + ResolutionNode resolved = null; + for (Iterator j = conflictResolvers.iterator(); + resolved == null && j.hasNext(); ) { + ConflictResolver conflictResolver = j.next(); + + resolved = conflictResolver.resolveConflict(previous, node); + } + + if (resolved == null) { + // TODO add better exception that can detail the two conflicting artifacts + ArtifactResolutionException are = new ArtifactResolutionException( + "Cannot resolve artifact version conflict between " + + previous.getArtifact().getVersion() + " and " + + node.getArtifact().getVersion(), + previous.getArtifact()); + + result.addVersionRangeViolation(are); + } + + if ((resolved != previous) && (resolved != node)) { + // TODO add better exception + result.addVersionRangeViolation(new ArtifactResolutionException( + "Conflict resolver returned unknown resolution node: ", resolved.getArtifact())); + } + + // TODO should this be part of mediation? + // previous one is more dominant + ResolutionNode nearest; + ResolutionNode farthest; + + if (resolved == previous) { + nearest = previous; + farthest = node; + } else { + nearest = node; + farthest = previous; + } + + if (checkScopeUpdate(farthest, nearest, listeners)) { + // if we need to update artifactScope of nearest to use farthest artifactScope, use the + // nearest version, but farthest artifactScope + nearest.disable(); + farthest.getArtifact() + .setVersion(nearest.getArtifact().getVersion()); + fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, nearest, farthest.getArtifact()); + } else { + farthest.disable(); + fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, farthest, nearest.getArtifact()); + } + } + } catch (OverConstrainedVersionException e) { + result.addVersionRangeViolation(e); + } + } + } else { + previousNodes = new ArrayList<>(); + + resolvedArtifacts.put(key, previousNodes); + } + previousNodes.add(node); + + if (node.isActive()) { + fireEvent(ResolutionListener.INCLUDE_ARTIFACT, listeners, node); + } + + // don't pull in the transitive deps of a system-scoped dependency. + if (node.isActive() && !Artifact.SCOPE_SYSTEM.equals(node.getArtifact().getScope())) { + fireEvent(ResolutionListener.PROCESS_CHILDREN, listeners, node); + + Artifact parentArtifact = node.getArtifact(); + + for (Iterator i = node.getChildrenIterator(); i.hasNext(); ) { + ResolutionNode child = i.next(); + + try { + + // We leave in optional ones, but don't pick up its dependencies + if (!child.isResolved() && (!child.getArtifact().isOptional() || child.isChildOfRootNode())) { + Artifact artifact = child.getArtifact(); + artifact.setDependencyTrail(node.getDependencyTrail()); + List childRemoteRepositories = child.getRemoteRepositories(); + + MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest(request); + metadataRequest.setArtifact(artifact); + metadataRequest.setRemoteRepositories(childRemoteRepositories); + + try { + ResolutionGroup rGroup; + + Object childKey; + do { + childKey = child.getKey(); + + if (managedVersions.containsKey(childKey)) { + // If this child node is a managed dependency, ensure + // we are using the dependency management version + // of this child if applicable b/c we want to use the + // managed version's POM, *not* any other version's POM. + // We retrieve the POM below in the retrieval step. + manageArtifact(child, managedVersions, listeners); + + // Also, we need to ensure that any exclusions it presents are + // added to the artifact before we retrieve the metadata + // for the artifact; otherwise we may end up with unwanted + // dependencies. + Artifact ma = managedVersions.get(childKey); + ArtifactFilter managedExclusionFilter = ma.getDependencyFilter(); + if (null != managedExclusionFilter) { + if (null != artifact.getDependencyFilter()) { + AndArtifactFilter aaf = new AndArtifactFilter(); + aaf.add(artifact.getDependencyFilter()); + aaf.add(managedExclusionFilter); + artifact.setDependencyFilter(aaf); + } else { + artifact.setDependencyFilter(managedExclusionFilter); + } + } + } + + if (artifact.getVersion() == null) { + // set the recommended version + // TODO maybe its better to just pass the range through to retrieval and use a + // transformation? + ArtifactVersion version; + if (!artifact.isSelectedVersionKnown()) { + List versions = artifact.getAvailableVersions(); + if (versions == null) { + versions = source.retrieveAvailableVersions(metadataRequest); + artifact.setAvailableVersions(versions); + } + + Collections.sort(versions); + + VersionRange versionRange = artifact.getVersionRange(); + + version = versionRange.matchVersion(versions); + + if (version == null) { + if (versions.isEmpty()) { + throw new OverConstrainedVersionException( + "No versions are present in the repository for the artifact" + + " with a range " + versionRange, + artifact, + childRemoteRepositories); + } + + throw new OverConstrainedVersionException( + "Couldn't find a version in " + versions + " to match range " + + versionRange, + artifact, + childRemoteRepositories); + } + } else { + version = artifact.getSelectedVersion(); + } + + artifact.selectVersion(version.toString()); + fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, child); + } + + rGroup = source.retrieve(metadataRequest); + + if (rGroup == null) { + break; + } + } while (!childKey.equals(child.getKey())); + + if (parentArtifact != null + && parentArtifact.getDependencyFilter() != null + && !parentArtifact.getDependencyFilter().include(artifact)) { + // MNG-3769: the [probably relocated] artifact is excluded. + // We could process exclusions on relocated artifact details in the + // MavenMetadataSource.createArtifacts(..) step, BUT that would + // require resolving the POM from the repository very early on in + // the build. + continue; + } + + // TODO might be better to have source.retrieve() throw a specific exception for this + // situation + // and catch here rather than have it return null + if (rGroup == null) { + // relocated dependency artifact is declared excluded, no need to add and recurse + // further + continue; + } + + child.addDependencies(rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter); + + } catch (CyclicDependencyException e) { + // would like to throw this, but we have crappy stuff in the repo + + fireEvent( + ResolutionListener.OMIT_FOR_CYCLE, + listeners, + new ResolutionNode(e.getArtifact(), childRemoteRepositories, child)); + } catch (ArtifactMetadataRetrievalException e) { + artifact.setDependencyTrail(node.getDependencyTrail()); + + throw new ArtifactResolutionException( + "Unable to get dependency information for " + artifact.getId() + ": " + + e.getMessage(), + artifact, + childRemoteRepositories, + e); + } + + ArtifactResolutionRequest subRequest = new ArtifactResolutionRequest(metadataRequest); + subRequest.setServers(request.getServers()); + subRequest.setMirrors(request.getMirrors()); + subRequest.setProxies(request.getProxies()); + recurse( + result, + child, + resolvedArtifacts, + managedVersions, + subRequest, + source, + filter, + listeners, + conflictResolvers); + } + } catch (OverConstrainedVersionException e) { + result.addVersionRangeViolation(e); + } catch (ArtifactResolutionException e) { + result.addMetadataResolutionException(e); + } + } + + fireEvent(ResolutionListener.FINISH_PROCESSING_CHILDREN, listeners, node); + } + } + + private void manageArtifact( + ResolutionNode node, ManagedVersionMap managedVersions, List listeners) { + Artifact artifact = managedVersions.get(node.getKey()); + + // Before we update the version of the artifact, we need to know + // whether we are working on a transitive dependency or not. This + // allows depMgmt to always override transitive dependencies, while + // explicit child override depMgmt (viz. depMgmt should only + // provide defaults to children, but should override transitives). + // We can do this by calling isChildOfRootNode on the current node. + if ((artifact.getVersion() != null) + && (!node.isChildOfRootNode() || node.getArtifact().getVersion() == null)) { + fireEvent(ResolutionListener.MANAGE_ARTIFACT_VERSION, listeners, node, artifact); + node.getArtifact().setVersion(artifact.getVersion()); + } + + if ((artifact.getScope() != null) + && (!node.isChildOfRootNode() || node.getArtifact().getScope() == null)) { + fireEvent(ResolutionListener.MANAGE_ARTIFACT_SCOPE, listeners, node, artifact); + node.getArtifact().setScope(artifact.getScope()); + } + + if (Artifact.SCOPE_SYSTEM.equals(node.getArtifact().getScope()) + && (node.getArtifact().getFile() == null) + && (artifact.getFile() != null)) { + fireEvent(ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH, listeners, node, artifact); + node.getArtifact().setFile(artifact.getFile()); + } + } + + /** + * Check if the artifactScope needs to be updated. More info. + * + * @param farthest farthest resolution node + * @param nearest nearest resolution node + * @param listeners + */ + boolean checkScopeUpdate(ResolutionNode farthest, ResolutionNode nearest, List listeners) { + boolean updateScope = false; + Artifact farthestArtifact = farthest.getArtifact(); + Artifact nearestArtifact = nearest.getArtifact(); + + /* farthest is runtime and nearest has lower priority, change to runtime */ + if (Artifact.SCOPE_RUNTIME.equals(farthestArtifact.getScope()) + && (Artifact.SCOPE_TEST.equals(nearestArtifact.getScope()) + || Artifact.SCOPE_PROVIDED.equals(nearestArtifact.getScope()))) { + updateScope = true; + } + + /* farthest is compile and nearest is not (has lower priority), change to compile */ + if (Artifact.SCOPE_COMPILE.equals(farthestArtifact.getScope()) + && !Artifact.SCOPE_COMPILE.equals(nearestArtifact.getScope())) { + updateScope = true; + } + + /* current POM rules all, if nearest is in current pom, do not update its artifactScope */ + if ((nearest.getDepth() < 2) && updateScope) { + updateScope = false; + + fireEvent(ResolutionListener.UPDATE_SCOPE_CURRENT_POM, listeners, nearest, farthestArtifact); + } + + if (updateScope) { + fireEvent(ResolutionListener.UPDATE_SCOPE, listeners, nearest, farthestArtifact); + + // previously we cloned the artifact, but it is more efficient to just update the artifactScope + // if problems are later discovered that the original object needs its original artifactScope value, + // cloning may + // again be appropriate + nearestArtifact.setScope(farthestArtifact.getScope()); + } + + return updateScope; + } + + private void fireEvent(int event, List listeners, ResolutionNode node) { + fireEvent(event, listeners, node, null); + } + + private void fireEvent(int event, List listeners, ResolutionNode node, Artifact replacement) { + fireEvent(event, listeners, node, replacement, null); + } + + private void fireEvent( + int event, + List listeners, + ResolutionNode node, + Artifact replacement, + VersionRange newRange) { + for (ResolutionListener listener : listeners) { + switch (event) { + case ResolutionListener.TEST_ARTIFACT: + listener.testArtifact(node.getArtifact()); + break; + case ResolutionListener.PROCESS_CHILDREN: + listener.startProcessChildren(node.getArtifact()); + break; + case ResolutionListener.FINISH_PROCESSING_CHILDREN: + listener.endProcessChildren(node.getArtifact()); + break; + case ResolutionListener.INCLUDE_ARTIFACT: + listener.includeArtifact(node.getArtifact()); + break; + case ResolutionListener.OMIT_FOR_NEARER: + listener.omitForNearer(node.getArtifact(), replacement); + break; + case ResolutionListener.OMIT_FOR_CYCLE: + listener.omitForCycle(node.getArtifact()); + break; + case ResolutionListener.UPDATE_SCOPE: + listener.updateScope(node.getArtifact(), replacement.getScope()); + break; + case ResolutionListener.UPDATE_SCOPE_CURRENT_POM: + listener.updateScopeCurrentPom(node.getArtifact(), replacement.getScope()); + break; + case ResolutionListener.MANAGE_ARTIFACT_VERSION: + if (listener instanceof ResolutionListenerForDepMgmt asImpl) { + asImpl.manageArtifactVersion(node.getArtifact(), replacement); + } else { + listener.manageArtifact(node.getArtifact(), replacement); + } + break; + case ResolutionListener.MANAGE_ARTIFACT_SCOPE: + if (listener instanceof ResolutionListenerForDepMgmt asImpl) { + asImpl.manageArtifactScope(node.getArtifact(), replacement); + } else { + listener.manageArtifact(node.getArtifact(), replacement); + } + break; + case ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH: + if (listener instanceof ResolutionListenerForDepMgmt asImpl) { + asImpl.manageArtifactSystemPath(node.getArtifact(), replacement); + } else { + listener.manageArtifact(node.getArtifact(), replacement); + } + break; + case ResolutionListener.SELECT_VERSION_FROM_RANGE: + listener.selectVersionFromRange(node.getArtifact()); + break; + case ResolutionListener.RESTRICT_RANGE: + if (node.getArtifact().getVersionRange().hasRestrictions() + || replacement.getVersionRange().hasRestrictions()) { + listener.restrictRange(node.getArtifact(), replacement, newRange); + } + break; + default: + throw new IllegalStateException("Unknown event: " + event); + } + } + } + + @Override + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners) { + return collect( + artifacts, + originatingArtifact, + managedVersions, + localRepository, + remoteRepositories, + source, + filter, + listeners, + null); + } + + public ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners) { + return collect( + artifacts, originatingArtifact, null, localRepository, remoteRepositories, source, filter, listeners); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/LegacyArtifactCollector.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/LegacyArtifactCollector.java new file mode 100644 index 000000000000..fcee2bc842d1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/LegacyArtifactCollector.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.ResolutionListener; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver; + +/** + * Artifact collector - takes a set of original artifacts and resolves the best versions to use + * along with their metadata. No artifacts are downloaded. + */ +@Deprecated +@SuppressWarnings("checkstyle:parameternumber") +public interface LegacyArtifactCollector { + + ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactResolutionRequest repositoryRequest, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners, + List conflictResolvers); + + ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners, + List conflictResolvers); + + // used by maven-dependency-tree and maven-dependency-plugin + @Deprecated + ArtifactResolutionResult collect( + Set artifacts, + Artifact originatingArtifact, + Map managedVersions, + ArtifactRepository localRepository, + List remoteRepositories, + ArtifactMetadataSource source, + ArtifactFilter filter, + List listeners); +} diff --git a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolver.java similarity index 83% rename from maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolver.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolver.java index 4d129b7e765a..00846bf5ee71 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolver.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolver.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.legacy.resolver.conflict; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,17 +16,16 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.legacy.resolver.conflict; import org.apache.maven.artifact.resolver.ResolutionNode; /** * Determines which version of an artifact to use when there are conflicting declarations. * - * @author Jason van Zyl - * @author Mark Hobson */ -public interface ConflictResolver -{ +@Deprecated +public interface ConflictResolver { String ROLE = ConflictResolver.class.getName(); /** @@ -40,5 +37,5 @@ public interface ConflictResolver * this conflict cannot be resolved * @since 3.0 */ - ResolutionNode resolveConflict( ResolutionNode node1, ResolutionNode node2 ); + ResolutionNode resolveConflict(ResolutionNode node1, ResolutionNode node2); } diff --git a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverFactory.java similarity index 84% rename from maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverFactory.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverFactory.java index 8f3f9f43e6ba..ecda46077c98 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverFactory.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverFactory.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.legacy.resolver.conflict; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,16 +16,16 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.legacy.resolver.conflict; /** * A factory that produces conflict resolvers of various types. * - * @author Mark Hobson * @see ConflictResolver * @since 3.0 */ -public interface ConflictResolverFactory -{ +@Deprecated +public interface ConflictResolverFactory { // constants -------------------------------------------------------------- /** The plexus role for this component. */ @@ -43,6 +41,5 @@ public interface ConflictResolverFactory * @throws ConflictResolverNotFoundException * if the specified type was not found */ - ConflictResolver getConflictResolver( String type ) - throws ConflictResolverNotFoundException; + ConflictResolver getConflictResolver(String type) throws ConflictResolverNotFoundException; } diff --git a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverNotFoundException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverNotFoundException.java similarity index 82% rename from maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverNotFoundException.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverNotFoundException.java index b5f61ed130e4..e8213c470143 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverNotFoundException.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/ConflictResolverNotFoundException.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.legacy.resolver.conflict; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,16 +16,15 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.legacy.resolver.conflict; /** * Indicates that a specified conflict resolver implementation could not be found. * - * @author Mark Hobson * @since 3.0 */ -public class ConflictResolverNotFoundException - extends Exception -{ +@Deprecated +public class ConflictResolverNotFoundException extends Exception { // constants -------------------------------------------------------------- /** The serial version ID. */ @@ -40,8 +37,7 @@ public class ConflictResolverNotFoundException * * @param message the message */ - public ConflictResolverNotFoundException( String message ) - { - super( message ); + public ConflictResolverNotFoundException(String message) { + super(message); } } diff --git a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolver.java similarity index 78% rename from maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolver.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolver.java index 76f1929db511..a3a06800f3ec 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolver.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolver.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.legacy.resolver.conflict; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,19 +16,18 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.legacy.resolver.conflict; -import org.codehaus.plexus.component.annotations.Component; +import javax.inject.Named; +import javax.inject.Singleton; /** * The default conflict resolver that delegates to the nearest strategy. * - * @author Jason van Zyl * @see NearestConflictResolver * @deprecated As of 3.0, use a specific implementation instead, e.g. {@link NearestConflictResolver} */ @Deprecated -@Component( role = ConflictResolver.class ) -public class DefaultConflictResolver - extends NearestConflictResolver -{ -} +@Named +@Singleton +public class DefaultConflictResolver extends NearestConflictResolver {} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolverFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolverFactory.java new file mode 100644 index 000000000000..c99034c82bbb --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/DefaultConflictResolverFactory.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.codehaus.plexus.PlexusConstants; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.context.Context; +import org.codehaus.plexus.context.ContextException; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; + +/** + * A conflict resolver factory that obtains instances from a plexus container. + * + * TODO you don't need the container in here with the active maps (jvz). + * @since 3.0 + */ +@Named +@Singleton +@Deprecated +public class DefaultConflictResolverFactory implements ConflictResolverFactory, Contextualizable { + // fields ----------------------------------------------------------------- + + /** + * The plexus container used to obtain instances from. + */ + @Inject + private PlexusContainer container; + + // ConflictResolverFactory methods ---------------------------------------- + + /* + * @see org.apache.maven.artifact.resolver.conflict.ConflictResolverFactory#getConflictResolver(java.lang.String) + */ + + @Override + public ConflictResolver getConflictResolver(String type) throws ConflictResolverNotFoundException { + try { + return (ConflictResolver) container.lookup(ConflictResolver.ROLE, type); + } catch (ComponentLookupException exception) { + throw new ConflictResolverNotFoundException("Cannot find conflict resolver of type: " + type); + } + } + + // Contextualizable methods ----------------------------------------------- + + /* + * @see org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable#contextualize(org.codehaus.plexus.context.Context) + */ + + @Override + public void contextualize(Context context) throws ContextException { + container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/FarthestConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/FarthestConflictResolver.java new file mode 100644 index 000000000000..e9ec08c4e699 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/FarthestConflictResolver.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.resolver.ResolutionNode; + +/** + * Resolves conflicting artifacts by always selecting the farthest declaration. Farthest is defined as the + * declaration that has the most transitive steps away from the project being built. + * + * @since 3.0 + */ +@Named("farthest") +@Singleton +@Deprecated +public class FarthestConflictResolver implements ConflictResolver { + // ConflictResolver methods ----------------------------------------------- + + /* + * @see org.apache.maven.artifact.resolver.conflict.ConflictResolver#resolveConflict(org.apache.maven.artifact.resolver.ResolutionNode, + * org.apache.maven.artifact.resolver.ResolutionNode) + */ + + @Override + public ResolutionNode resolveConflict(ResolutionNode node1, ResolutionNode node2) { + return node1.getDepth() >= node2.getDepth() ? node1 : node2; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/NearestConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/NearestConflictResolver.java new file mode 100644 index 000000000000..6d513207bccd --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/NearestConflictResolver.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.resolver.ResolutionNode; + +/** + * Resolves conflicting artifacts by always selecting the nearest declaration. Nearest is defined as the + * declaration that has the least transitive steps away from the project being built. + * + * @since 3.0 + */ +@Named("nearest") +@Singleton +@Deprecated +public class NearestConflictResolver implements ConflictResolver { + // ConflictResolver methods ----------------------------------------------- + + /* + * @see org.apache.maven.artifact.resolver.conflict.ConflictResolver#resolveConflict(org.apache.maven.artifact.resolver.ResolutionNode, + * org.apache.maven.artifact.resolver.ResolutionNode) + */ + + @Override + public ResolutionNode resolveConflict(ResolutionNode node1, ResolutionNode node2) { + return node1.getDepth() <= node2.getDepth() ? node1 : node2; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/NewestConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/NewestConflictResolver.java new file mode 100644 index 000000000000..bf0572870a62 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/NewestConflictResolver.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; + +/** + * Resolves conflicting artifacts by always selecting the newest declaration. Newest is defined as the + * declaration whose version is greater according to ArtifactVersion.compareTo. + * + * @see ArtifactVersion#compareTo + * @since 3.0 + */ +@Named("newest") +@Singleton +@Deprecated +public class NewestConflictResolver implements ConflictResolver { + // ConflictResolver methods ----------------------------------------------- + + /* + * @see org.apache.maven.artifact.resolver.conflict.ConflictResolver#resolveConflict(org.apache.maven.artifact.resolver.ResolutionNode, + * org.apache.maven.artifact.resolver.ResolutionNode) + */ + + @Override + public ResolutionNode resolveConflict(ResolutionNode node1, ResolutionNode node2) { + try { + ArtifactVersion version1 = node1.getArtifact().getSelectedVersion(); + ArtifactVersion version2 = node2.getArtifact().getSelectedVersion(); + + return version1.compareTo(version2) > 0 ? node1 : node2; + } catch (OverConstrainedVersionException exception) { + // TODO log message or throw exception? + + return null; + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/OldestConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/OldestConflictResolver.java new file mode 100644 index 000000000000..fabc790e2883 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/conflict/OldestConflictResolver.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; + +/** + * Resolves conflicting artifacts by always selecting the oldest declaration. Oldest is defined as the + * declaration whose version is less according to ArtifactVersion.compareTo. + * + * @see ArtifactVersion#compareTo + * @since 3.0 + */ +@Named("oldest") +@Singleton +@Deprecated +public class OldestConflictResolver implements ConflictResolver { + // ConflictResolver methods ----------------------------------------------- + + /* + * @see org.apache.maven.artifact.resolver.conflict.ConflictResolver#resolveConflict(org.apache.maven.artifact.resolver.ResolutionNode, + * org.apache.maven.artifact.resolver.ResolutionNode) + */ + + @Override + public ResolutionNode resolveConflict(ResolutionNode node1, ResolutionNode node2) { + try { + ArtifactVersion version1 = node1.getArtifact().getSelectedVersion(); + ArtifactVersion version2 = node2.getArtifact().getSelectedVersion(); + + return version1.compareTo(version2) <= 0 ? node1 : node2; + } catch (OverConstrainedVersionException exception) { + // TODO log message or throw exception? + + return null; + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/AbstractVersionTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/AbstractVersionTransformation.java new file mode 100644 index 000000000000..c86e9cd1369d --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/AbstractVersionTransformation.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import javax.inject.Inject; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultRepositoryRequest; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException; +import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.repository.legacy.WagonManager; +import org.codehaus.plexus.logging.AbstractLogEnabled; + +/** + * Describes a version transformation during artifact resolution. + * + * TODO try and refactor to remove abstract methods - not particular happy about current design + */ +@Deprecated +public abstract class AbstractVersionTransformation extends AbstractLogEnabled implements ArtifactTransformation { + @Inject + protected RepositoryMetadataManager repositoryMetadataManager; + + @Inject + protected WagonManager wagonManager; + + @Override + public void transformForResolve( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException { + RepositoryRequest request = new DefaultRepositoryRequest(); + request.setLocalRepository(localRepository); + request.setRemoteRepositories(remoteRepositories); + transformForResolve(artifact, request); + } + + protected String resolveVersion( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws RepositoryMetadataResolutionException { + RepositoryRequest request = new DefaultRepositoryRequest(); + request.setLocalRepository(localRepository); + request.setRemoteRepositories(remoteRepositories); + return resolveVersion(artifact, request); + } + + protected String resolveVersion(Artifact artifact, RepositoryRequest request) + throws RepositoryMetadataResolutionException { + RepositoryMetadata metadata; + // Don't use snapshot metadata for LATEST (which isSnapshot returns true for) + if (!artifact.isSnapshot() || Artifact.LATEST_VERSION.equals(artifact.getBaseVersion())) { + metadata = new ArtifactRepositoryMetadata(artifact); + } else { + metadata = new SnapshotArtifactRepositoryMetadata(artifact); + } + + repositoryMetadataManager.resolve(metadata, request); + + artifact.addMetadata(metadata); + + Metadata repoMetadata = metadata.getMetadata(); + String version = null; + if (repoMetadata != null && repoMetadata.getVersioning() != null) { + version = constructVersion(repoMetadata.getVersioning(), artifact.getBaseVersion()); + } + + if (version == null) { + // use the local copy, or if it doesn't exist - go to the remote repo for it + version = artifact.getBaseVersion(); + } + + // TODO also do this logging for other metadata? + // TODO figure out way to avoid duplicated message + if (getLogger().isDebugEnabled()) { + if (!version.equals(artifact.getBaseVersion())) { + String message = artifact.getArtifactId() + ": resolved to version " + version; + if (artifact.getRepository() != null) { + message += " from repository " + artifact.getRepository().getId(); + } else { + message += " from local repository"; + } + getLogger().debug(message); + } else { + // Locally installed file is newer, don't use the resolved version + getLogger().debug(artifact.getArtifactId() + ": using locally installed snapshot"); + } + } + return version; + } + + protected abstract String constructVersion(Versioning versioning, String baseVersion); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ArtifactTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ArtifactTransformation.java new file mode 100644 index 000000000000..ddda6402f6ec --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ArtifactTransformation.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.deployer.ArtifactDeploymentException; +import org.apache.maven.artifact.installer.ArtifactInstallationException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + */ +@Deprecated +public interface ArtifactTransformation { + String ROLE = ArtifactTransformation.class.getName(); + + /** + * Take in an artifact and return the transformed artifact for locating in the remote repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param request the repositories to check + */ + void transformForResolve(Artifact artifact, RepositoryRequest request) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Take in an artifact and return the transformed artifact for locating in the remote repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param remoteRepositories the repositories to check + * @param localRepository the local repository + */ + void transformForResolve( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Take in an artifact and return the transformed artifact for locating in the local repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param localRepository the local repository it will be stored in + */ + void transformForInstall(Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException; + + /** + * Take in an artifact and return the transformed artifact for distributing to remote repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param remoteRepository the repository to deploy to + * @param localRepository the local repository + */ + void transformForDeployment( + Artifact artifact, ArtifactRepository remoteRepository, ArtifactRepository localRepository) + throws ArtifactDeploymentException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ArtifactTransformationManager.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ArtifactTransformationManager.java new file mode 100644 index 000000000000..f86d9a16c3f2 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ArtifactTransformationManager.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.deployer.ArtifactDeploymentException; +import org.apache.maven.artifact.installer.ArtifactInstallationException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + * Manages multiple ArtifactTransformation instances and applies them in succession. + */ +@Deprecated +public interface ArtifactTransformationManager { + String ROLE = ArtifactTransformationManager.class.getName(); + + /** + * Take in an artifact and return the transformed artifact for locating in the remote repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param request the repositories to check + */ + void transformForResolve(Artifact artifact, RepositoryRequest request) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Take in an artifact and return the transformed artifact for locating in the remote repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param remoteRepositories the repositories to check + * @param localRepository the local repository + */ + void transformForResolve( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException; + + /** + * Take in an artifact and return the transformed artifact for locating in the local repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param localRepository the local repository it will be stored in + */ + void transformForInstall(Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException; + + /** + * Take in an artifact and return the transformed artifact for distributing to a remote repository. If no + * transformation has occurred the original artifact is returned. + * + * @param artifact Artifact to be transformed. + * @param remoteRepository the repository to deploy to + * @param localRepository the local repository the metadata is stored in + */ + void transformForDeployment( + Artifact artifact, ArtifactRepository remoteRepository, ArtifactRepository localRepository) + throws ArtifactDeploymentException; + + List getArtifactTransformations(); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/DefaultArtifactTransformationManager.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/DefaultArtifactTransformationManager.java new file mode 100644 index 000000000000..3aa99257dac1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/DefaultArtifactTransformationManager.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.deployer.ArtifactDeploymentException; +import org.apache.maven.artifact.installer.ArtifactInstallationException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + */ +@Named +@Singleton +@Deprecated +public class DefaultArtifactTransformationManager implements ArtifactTransformationManager { + + private List artifactTransformations; + + @Inject + public DefaultArtifactTransformationManager(Map artifactTransformations) { + this.artifactTransformations = Stream.of("release", "latest", "snapshot") + .map(artifactTransformations::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + @Override + public void transformForResolve(Artifact artifact, RepositoryRequest request) + throws ArtifactResolutionException, ArtifactNotFoundException { + for (ArtifactTransformation transform : artifactTransformations) { + transform.transformForResolve(artifact, request); + } + } + + @Override + public void transformForResolve( + Artifact artifact, List remoteRepositories, ArtifactRepository localRepository) + throws ArtifactResolutionException, ArtifactNotFoundException { + for (ArtifactTransformation transform : artifactTransformations) { + transform.transformForResolve(artifact, remoteRepositories, localRepository); + } + } + + @Override + public void transformForInstall(Artifact artifact, ArtifactRepository localRepository) + throws ArtifactInstallationException { + for (ArtifactTransformation transform : artifactTransformations) { + transform.transformForInstall(artifact, localRepository); + } + } + + @Override + public void transformForDeployment( + Artifact artifact, ArtifactRepository remoteRepository, ArtifactRepository localRepository) + throws ArtifactDeploymentException { + for (ArtifactTransformation transform : artifactTransformations) { + transform.transformForDeployment(artifact, remoteRepository, localRepository); + } + } + + @Override + public List getArtifactTransformations() { + return artifactTransformations; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/LatestArtifactTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/LatestArtifactTransformation.java new file mode 100644 index 000000000000..065bad156a17 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/LatestArtifactTransformation.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + * Describes a version transformation during artifact resolution - "latest" type + */ +@Named("latest") +@Singleton +@Deprecated +public class LatestArtifactTransformation extends AbstractVersionTransformation { + + @Override + public void transformForResolve(Artifact artifact, RepositoryRequest request) + throws ArtifactResolutionException, ArtifactNotFoundException { + if (Artifact.LATEST_VERSION.equals(artifact.getVersion())) { + try { + String version = resolveVersion(artifact, request); + if (Artifact.LATEST_VERSION.equals(version)) { + throw new ArtifactNotFoundException("Unable to determine the latest version", artifact); + } + + artifact.setBaseVersion(version); + artifact.updateVersion(version, request.getLocalRepository()); + } catch (RepositoryMetadataResolutionException e) { + throw new ArtifactResolutionException(e.getMessage(), artifact, e); + } + } + } + + @Override + public void transformForInstall(Artifact artifact, ArtifactRepository localRepository) { + // metadata is added via addPluginArtifactMetadata + } + + @Override + public void transformForDeployment( + Artifact artifact, ArtifactRepository remoteRepository, ArtifactRepository localRepository) { + // metadata is added via addPluginArtifactMetadata + } + + @Override + protected String constructVersion(Versioning versioning, String baseVersion) { + return versioning.getLatest(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ReleaseArtifactTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ReleaseArtifactTransformation.java new file mode 100644 index 000000000000..8eb749f8125f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/ReleaseArtifactTransformation.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + * Change the version RELEASE to the appropriate release version from the remote repository. + * + */ +@Named("release") +@Singleton +@Deprecated +public class ReleaseArtifactTransformation extends AbstractVersionTransformation { + + @Override + public void transformForResolve(Artifact artifact, RepositoryRequest request) + throws ArtifactResolutionException, ArtifactNotFoundException { + if (Artifact.RELEASE_VERSION.equals(artifact.getVersion())) { + try { + String version = resolveVersion(artifact, request); + + if (Artifact.RELEASE_VERSION.equals(version)) { + throw new ArtifactNotFoundException("Unable to determine the release version", artifact); + } + + artifact.setBaseVersion(version); + artifact.updateVersion(version, request.getLocalRepository()); + } catch (RepositoryMetadataResolutionException e) { + throw new ArtifactResolutionException(e.getMessage(), artifact, e); + } + } + } + + @Override + public void transformForInstall(Artifact artifact, ArtifactRepository localRepository) { + ArtifactMetadata metadata = createMetadata(artifact); + + artifact.addMetadata(metadata); + } + + @Override + public void transformForDeployment( + Artifact artifact, ArtifactRepository remoteRepository, ArtifactRepository localRepository) { + ArtifactMetadata metadata = createMetadata(artifact); + + artifact.addMetadata(metadata); + } + + private ArtifactMetadata createMetadata(Artifact artifact) { + Versioning versioning = new Versioning(); + // TODO Should this be changed for MNG-6754 too? + versioning.updateTimestamp(); + versioning.addVersion(artifact.getVersion()); + + if (artifact.isRelease()) { + versioning.setRelease(artifact.getVersion()); + } + + return new ArtifactRepositoryMetadata(artifact, versioning); + } + + @Override + protected String constructVersion(Versioning versioning, String baseVersion) { + return versioning.getRelease(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/SnapshotTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/SnapshotTransformation.java new file mode 100644 index 000000000000..3c14203b35c2 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/transform/SnapshotTransformation.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.transform; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.deployer.ArtifactDeploymentException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException; +import org.apache.maven.artifact.repository.metadata.Snapshot; +import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; + +/** + */ +@Named("snapshot") +@Singleton +@Deprecated +public class SnapshotTransformation extends AbstractVersionTransformation { + private static final String DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT = "yyyyMMdd.HHmmss"; + + private static final TimeZone DEFAULT_SNAPSHOT_TIME_ZONE = TimeZone.getTimeZone("Etc/UTC"); + + private String deploymentTimestamp; + + @Override + public void transformForResolve(Artifact artifact, RepositoryRequest request) throws ArtifactResolutionException { + // Only select snapshots that are unresolved (eg 1.0-SNAPSHOT, not 1.0-20050607.123456) + if (artifact.isSnapshot() && artifact.getBaseVersion().equals(artifact.getVersion())) { + try { + String version = resolveVersion(artifact, request); + artifact.updateVersion(version, request.getLocalRepository()); + } catch (RepositoryMetadataResolutionException e) { + throw new ArtifactResolutionException(e.getMessage(), artifact, e); + } + } + } + + @Override + public void transformForInstall(Artifact artifact, ArtifactRepository localRepository) { + if (artifact.isSnapshot()) { + Snapshot snapshot = new Snapshot(); + snapshot.setLocalCopy(true); + RepositoryMetadata metadata = new SnapshotArtifactRepositoryMetadata(artifact, snapshot); + + artifact.addMetadata(metadata); + } + } + + @Override + public void transformForDeployment( + Artifact artifact, ArtifactRepository remoteRepository, ArtifactRepository localRepository) + throws ArtifactDeploymentException { + if (artifact.isSnapshot()) { + Snapshot snapshot = new Snapshot(); + + // TODO Should this be changed for MNG-6754 too? + snapshot.setTimestamp(getDeploymentTimestamp()); + + // we update the build number anyway so that it doesn't get lost. It requires the timestamp to take effect + try { + int buildNumber = resolveLatestSnapshotBuildNumber(artifact, localRepository, remoteRepository); + + snapshot.setBuildNumber(buildNumber + 1); + } catch (RepositoryMetadataResolutionException e) { + throw new ArtifactDeploymentException( + "Error retrieving previous build number for artifact '" + artifact.getDependencyConflictId() + + "': " + e.getMessage(), + e); + } + + RepositoryMetadata metadata = new SnapshotArtifactRepositoryMetadata(artifact, snapshot); + + artifact.setResolvedVersion( + constructVersion(metadata.getMetadata().getVersioning(), artifact.getBaseVersion())); + + artifact.addMetadata(metadata); + } + } + + public String getDeploymentTimestamp() { + if (deploymentTimestamp == null) { + deploymentTimestamp = getUtcDateFormatter().format(new Date()); + } + return deploymentTimestamp; + } + + @Override + protected String constructVersion(Versioning versioning, String baseVersion) { + String version = null; + Snapshot snapshot = versioning.getSnapshot(); + if (snapshot != null) { + if (snapshot.getTimestamp() != null && snapshot.getBuildNumber() > 0) { + String newVersion = snapshot.getTimestamp() + "-" + snapshot.getBuildNumber(); + version = baseVersion.replace(Artifact.SNAPSHOT_VERSION, newVersion); + } else { + version = baseVersion; + } + } + return version; + } + + private int resolveLatestSnapshotBuildNumber( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws RepositoryMetadataResolutionException { + RepositoryMetadata metadata = new SnapshotArtifactRepositoryMetadata(artifact); + + getLogger().info("Retrieving previous build number from " + remoteRepository.getId()); + repositoryMetadataManager.resolveAlways(metadata, localRepository, remoteRepository); + + int buildNumber = 0; + Metadata repoMetadata = metadata.getMetadata(); + if ((repoMetadata != null) + && (repoMetadata.getVersioning() != null + && repoMetadata.getVersioning().getSnapshot() != null)) { + buildNumber = repoMetadata.getVersioning().getSnapshot().getBuildNumber(); + } + return buildNumber; + } + + public static DateFormat getUtcDateFormatter() { + DateFormat utcDateFormatter = new SimpleDateFormat(DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT); + utcDateFormatter.setCalendar(new GregorianCalendar()); + utcDateFormatter.setTimeZone(DEFAULT_SNAPSHOT_TIME_ZONE); + return utcDateFormatter; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ArtifactMetadata.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ArtifactMetadata.java new file mode 100644 index 000000000000..a808e2be8c88 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ArtifactMetadata.java @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import java.util.Collection; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * Artifact Metadata that is resolved independent of Artifact itself. + * + */ +@Deprecated +public class ArtifactMetadata { + /** + * standard glorified artifact coordinates + */ + protected String groupId; + + protected String artifactId; + protected String version; + protected String type; + protected ArtifactScopeEnum artifactScope; + protected String classifier; + + /** + * explanation: why this MD was chosen over its siblings + * in the resulting structure (classpath for now) + */ + protected String why; + + /** dependencies of the artifact behind this metadata */ + protected Collection dependencies; + + /** metadata URI */ + protected String uri; + + /** is metadata found anywhere */ + protected boolean resolved = false; + + /** does the actual artifact for this metadata exists */ + protected boolean artifactExists = false; + /** artifact URI */ + protected String artifactUri; + + /** error message */ + private String error; + + // ------------------------------------------------------------------ + /** + * + */ + public ArtifactMetadata(String name) { + if (name == null) { + return; + } + int ind1 = name.indexOf(':'); + int ind2 = name.lastIndexOf(':'); + + if (ind1 == -1 || ind2 == -1) { + return; + } + + this.groupId = name.substring(0, ind1); + if (ind1 == ind2) { + this.artifactId = name.substring(ind1 + 1); + } else { + this.artifactId = name.substring(ind1 + 1, ind2); + this.version = name.substring(ind2 + 1); + } + } + + public ArtifactMetadata(String groupId, String name, String version) { + this(groupId, name, version, null); + } + + public ArtifactMetadata(String groupId, String name, String version, String type) { + this(groupId, name, version, type, null); + } + + public ArtifactMetadata(String groupId, String name, String version, String type, ArtifactScopeEnum artifactScope) { + this(groupId, name, version, type, artifactScope, null); + } + + public ArtifactMetadata( + String groupId, + String name, + String version, + String type, + ArtifactScopeEnum artifactScope, + String classifier) { + this(groupId, name, version, type, artifactScope, classifier, null); + } + + public ArtifactMetadata( + String groupId, + String name, + String version, + String type, + ArtifactScopeEnum artifactScope, + String classifier, + String artifactUri) { + this(groupId, name, version, type, artifactScope, classifier, artifactUri, null, true, null); + } + + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactMetadata( + String groupId, + String name, + String version, + String type, + ArtifactScopeEnum artifactScope, + String classifier, + String artifactUri, + String why, + boolean resolved, + String error) { + this.groupId = groupId; + this.artifactId = name; + this.version = version; + this.type = type; + this.artifactScope = artifactScope; + this.classifier = classifier; + this.artifactUri = artifactUri; + this.why = why; + this.resolved = resolved; + this.error = error; + } + + @SuppressWarnings("checkstyle:parameternumber") + public ArtifactMetadata( + String groupId, + String name, + String version, + String type, + String scopeString, + String classifier, + String artifactUri, + String why, + boolean resolved, + String error) { + this( + groupId, + name, + version, + type, + scopeString == null ? ArtifactScopeEnum.DEFAULT_SCOPE : ArtifactScopeEnum.valueOf(scopeString), + classifier, + artifactUri, + why, + resolved, + error); + } + + public ArtifactMetadata(Artifact af) {} + + @Override + public String toString() { + return groupId + ":" + artifactId + ":" + version; + } + + public String toDomainString() { + return groupId + ":" + artifactId; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String name) { + this.artifactId = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getType() { + return type; + } + + public String getCheckedType() { + return type == null ? "jar" : type; + } + + public void setType(String type) { + this.type = type; + } + + public ArtifactScopeEnum getArtifactScope() { + return artifactScope == null ? ArtifactScopeEnum.DEFAULT_SCOPE : artifactScope; + } + + public void setArtifactScope(ArtifactScopeEnum artifactScope) { + this.artifactScope = artifactScope; + } + + public void setScope(String scope) { + this.artifactScope = scope == null ? ArtifactScopeEnum.DEFAULT_SCOPE : ArtifactScopeEnum.valueOf(scope); + } + + public String getClassifier() { + return classifier; + } + + public void setClassifier(String classifier) { + this.classifier = classifier; + } + + public boolean isResolved() { + return resolved; + } + + public void setResolved(boolean resolved) { + this.resolved = resolved; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getScope() { + return getArtifactScope().getScope(); + } + + public ArtifactScopeEnum getScopeAsEnum() { + return artifactScope == null ? ArtifactScopeEnum.DEFAULT_SCOPE : artifactScope; + } + + public boolean isArtifactExists() { + return artifactExists; + } + + public void setArtifactExists(boolean artifactExists) { + this.artifactExists = artifactExists; + } + + public Collection getDependencies() { + return dependencies; + } + + public void setDependencies(Collection dependencies) { + this.dependencies = dependencies; + } + + public String getArtifactUri() { + return artifactUri; + } + + public void setArtifactUri(String artifactUri) { + this.artifactUri = artifactUri; + } + + public String getWhy() { + return why; + } + + public void setWhy(String why) { + this.why = why; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public boolean isError() { + return error == null; + } + + public String getDependencyConflictId() { + return groupId + ":" + artifactId; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java new file mode 100644 index 000000000000..37c6b9f4235f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * classpath container that is aware of the classpath scope + * + * + */ +@Deprecated +public class ClasspathContainer implements Iterable { + private List classpath; + + private ArtifactScopeEnum scope; + + // ------------------------------------------------------------------------------------------- + public ClasspathContainer(ArtifactScopeEnum scope) { + this.scope = ArtifactScopeEnum.checkScope(scope); + } + + // ------------------------------------------------------------------------------------------- + public ClasspathContainer(List classpath, ArtifactScopeEnum scope) { + this(scope); + this.classpath = classpath; + } + + // ------------------------------------------------------------------------------------------- + @Override + public Iterator iterator() { + return classpath == null ? null : classpath.iterator(); + } + + // ------------------------------------------------------------------------------------------- + public ClasspathContainer add(ArtifactMetadata md) { + if (classpath == null) { + classpath = new ArrayList<>(16); + } + + classpath.add(md); + + return this; + } + + // ------------------------------------------------------------------------------------------- + public List getClasspath() { + return classpath; + } + + // ------------------------------------------------------------------------------------------- + public MetadataTreeNode getClasspathAsTree() throws MetadataResolutionException { + if (classpath == null || classpath.isEmpty()) { + return null; + } + + MetadataTreeNode tree = null; + MetadataTreeNode parent = null; + + for (ArtifactMetadata md : classpath) { + MetadataTreeNode node = new MetadataTreeNode(md, parent, md.isResolved(), md.getArtifactScope()); + if (tree == null) { + tree = node; + } + + if (parent != null) { + parent.setNChildren(1); + parent.addChild(0, node); + } + + parent = node; + } + return tree; + } + + public void setClasspath(List classpath) { + this.classpath = classpath; + } + + public ArtifactScopeEnum getScope() { + return scope; + } + + public void setScope(ArtifactScopeEnum scope) { + this.scope = scope; + } + + // ------------------------------------------------------------------------------------------- + @Override + public String toString() { + StringBuilder sb = new StringBuilder(256); + sb.append("[scope=").append(scope.getScope()); + if (classpath != null) { + for (ArtifactMetadata md : classpath) { + sb.append(": ") + .append(md.toString()) + .append('{') + .append(md.getArtifactUri()) + .append('}'); + } + } + sb.append(']'); + return sb.toString(); + } + // ------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------- +} diff --git a/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathTransformation.java similarity index 78% rename from maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathTransformation.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathTransformation.java index eece41330231..746ea81a0ff0 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathTransformation.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathTransformation.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.metadata; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,17 +16,17 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.metadata; import org.apache.maven.artifact.ArtifactScopeEnum; /** - * Helper class to conver an Md Graph into some form of a classpath + * Helper class to convert a metadata Graph into some form of a classpath * - * @author Oleg Gusakov * */ -public interface ClasspathTransformation -{ +@Deprecated +public interface ClasspathTransformation { String ROLE = ClasspathTransformation.class.getName(); /** @@ -41,6 +39,6 @@ public interface ClasspathTransformation * @return Collection of metadata objects in the linked subgraph of the graph which * contains the graph.getEntry() vertice */ - ClasspathContainer transform( MetadataGraph dirtyGraph, ArtifactScopeEnum scope, boolean resolve ) - throws MetadataGraphTransformationException; + ClasspathContainer transform(MetadataGraph dirtyGraph, ArtifactScopeEnum scope, boolean resolve) + throws MetadataGraphTransformationException; } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java new file mode 100644 index 000000000000..e7bc8f9f9694 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * default implementation of the metadata classpath transformer + * + * + */ +@Named +@Singleton +@Deprecated +public class DefaultClasspathTransformation implements ClasspathTransformation { + @Inject + GraphConflictResolver conflictResolver; + + // ---------------------------------------------------------------------------------------------------- + @Override + public ClasspathContainer transform(MetadataGraph dirtyGraph, ArtifactScopeEnum scope, boolean resolve) + throws MetadataGraphTransformationException { + try { + if (dirtyGraph == null || dirtyGraph.isEmpty()) { + return null; + } + + MetadataGraph cleanGraph = conflictResolver.resolveConflicts(dirtyGraph, scope); + + if (cleanGraph == null || cleanGraph.isEmpty()) { + return null; + } + + ClasspathContainer cpc = new ClasspathContainer(scope); + if (cleanGraph.isEmptyEdges()) { + // single entry in the classpath, populated from itself + ArtifactMetadata amd = cleanGraph.getEntry().getMd(); + cpc.add(amd); + } else { + ClasspathGraphVisitor v = new ClasspathGraphVisitor(cleanGraph, cpc); + MetadataGraphVertex entry = cleanGraph.getEntry(); + // entry point + v.visit(entry); + } + + return cpc; + } catch (GraphConflictResolutionException e) { + throw new MetadataGraphTransformationException(e); + } + } + + // =================================================================================================== + /** + * Helper class to traverse graph. Required to make the containing method thread-safe + * and yet use class level data to lessen stack usage in recursion + */ + private class ClasspathGraphVisitor { + MetadataGraph graph; + + ClasspathContainer cpc; + + List visited; + + // ----------------------------------------------------------------------- + protected ClasspathGraphVisitor(MetadataGraph cleanGraph, ClasspathContainer cpc) { + this.cpc = cpc; + this.graph = cleanGraph; + + visited = new ArrayList<>(cleanGraph.getVertices().size()); + } + + // ----------------------------------------------------------------------- + protected void visit(MetadataGraphVertex node) { + ArtifactMetadata md = node.getMd(); + if (visited.contains(node)) { + return; + } + + cpc.add(md); + + List exits = graph.getExcidentEdges(node); + + if (exits != null && !exits.isEmpty()) { + MetadataGraphEdge[] sortedExits = exits.toArray(new MetadataGraphEdge[0]); + Arrays.sort(sortedExits, (e1, e2) -> { + if (e1.getDepth() == e2.getDepth()) { + if (e2.getPomOrder() == e1.getPomOrder()) { + return e1.getTarget() + .toString() + .compareTo(e2.getTarget().toString()); + } + return e2.getPomOrder() - e1.getPomOrder(); + } + return e2.getDepth() - e1.getDepth(); + }); + + for (MetadataGraphEdge e : sortedExits) { + MetadataGraphVertex targetNode = e.getTarget(); + targetNode.getMd().setArtifactScope(e.getScope()); + targetNode.getMd().setWhy(e.getSource().getMd().toString()); + visit(targetNode); + } + } + } + // ----------------------------------------------------------------------- + // ----------------------------------------------------------------------- + } + // ---------------------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------- +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolutionPolicy.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolutionPolicy.java new file mode 100644 index 000000000000..fbb50979b6c1 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolutionPolicy.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.codehaus.plexus.component.annotations.Configuration; + +/** + * + */ +@Named +@Singleton +@Deprecated +public class DefaultGraphConflictResolutionPolicy implements GraphConflictResolutionPolicy { + /** + * artifact, closer to the entry point, is selected + */ + @Configuration(name = "closer-first", value = "true") + private boolean closerFirst = true; + + /** + * newer artifact is selected + */ + @Configuration(name = "newer-first", value = "true") + private boolean newerFirst = true; + + @Override + public MetadataGraphEdge apply(MetadataGraphEdge e1, MetadataGraphEdge e2) { + int depth1 = e1.getDepth(); + int depth2 = e2.getDepth(); + + if (depth1 == depth2) { + ArtifactVersion v1 = new DefaultArtifactVersion(e1.getVersion()); + ArtifactVersion v2 = new DefaultArtifactVersion(e2.getVersion()); + + if (newerFirst) { + return v1.compareTo(v2) > 0 ? e1 : e2; + } + + return v1.compareTo(v2) > 0 ? e2 : e1; + } + + if (closerFirst) { + return depth1 < depth2 ? e1 : e2; + } + + return depth1 < depth2 ? e2 : e1; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java new file mode 100644 index 000000000000..d4d3a1e3a1b2 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * Default conflict resolver.Implements closer newer first policy by default, but could be configured via plexus + * + */ +@Named +@Singleton +@Deprecated +public class DefaultGraphConflictResolver implements GraphConflictResolver { + /** + * artifact, closer to the entry point, is selected + */ + @Inject + protected GraphConflictResolutionPolicy policy; + + // ------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------- + @Override + public MetadataGraph resolveConflicts(MetadataGraph graph, ArtifactScopeEnum scope) + throws GraphConflictResolutionException { + if (policy == null) { + throw new GraphConflictResolutionException("no GraphConflictResolutionPolicy injected"); + } + + if (graph == null) { + return null; + } + + final MetadataGraphVertex entry = graph.getEntry(); + if (entry == null) { + return null; + } + + if (graph.isEmpty()) { + throw new GraphConflictResolutionException("graph with an entry, but not vertices do not exist"); + } + + if (graph.isEmptyEdges()) { + return null; // no edges - nothing to worry about + } + + final TreeSet vertices = graph.getVertices(); + + try { + // edge case - single vertex graph + if (vertices.size() == 1) { + return new MetadataGraph(entry); + } + + final ArtifactScopeEnum requestedScope = ArtifactScopeEnum.checkScope(scope); + + MetadataGraph res = new MetadataGraph(vertices.size()); + res.setVersionedVertices(false); + res.setScopedVertices(false); + + MetadataGraphVertex resEntry = res.addVertex(entry.getMd()); + res.setEntry(resEntry); + + res.setScope(requestedScope); + + for (MetadataGraphVertex v : vertices) { + final List ins = graph.getIncidentEdges(v); + final MetadataGraphEdge edge = cleanEdges(v, ins, requestedScope); + + if (edge == null) { // no edges - don't need this vertex anymore + if (entry.equals(v)) { // unless it's an entry point. + // currently processing the entry point - it should not have any entry incident edges + res.getEntry().getMd().setWhy("This is a graph entry point. No links."); + } + } else { + // fill in domain md with actual version data + ArtifactMetadata md = v.getMd(); + ArtifactMetadata newMd = new ArtifactMetadata( + md.getGroupId(), + md.getArtifactId(), + edge.getVersion(), + md.getType(), + md.getScopeAsEnum(), + md.getClassifier(), + edge.getArtifactUri(), + edge.getSource() == null + ? "" + : edge.getSource().getMd().toString(), + edge.isResolved(), + edge.getTarget() == null + ? null + : edge.getTarget().getMd().getError()); + MetadataGraphVertex newV = res.addVertex(newMd); + MetadataGraphVertex sourceV = res.addVertex(edge.getSource().getMd()); + + res.addEdge(sourceV, newV, edge); + } + } + return findLinkedSubgraph(res); + } catch (MetadataResolutionException e) { + throw new GraphConflictResolutionException(e); + } + } + + // ------------------------------------------------------------------------------------- + private MetadataGraph findLinkedSubgraph(MetadataGraph g) { + if (g.getVertices().size() == 1) { + return g; + } + + List visited = new ArrayList<>(g.getVertices().size()); + visit(g.getEntry(), visited, g); + + List dropList = new ArrayList<>(g.getVertices().size()); + + // collect drop list + for (MetadataGraphVertex v : g.getVertices()) { + if (!visited.contains(v)) { + dropList.add(v); + } + } + + if (dropList.isEmpty()) { + return g; + } + + // now - drop vertices + TreeSet vertices = g.getVertices(); + for (MetadataGraphVertex v : dropList) { + vertices.remove(v); + } + + return g; + } + + // ------------------------------------------------------------------------------------- + private void visit(MetadataGraphVertex from, List visited, MetadataGraph graph) { + if (visited.contains(from)) { + return; + } + + visited.add(from); + + List exitList = graph.getExcidentEdges(from); + // String s = "|---> "+from.getMd().toString()+" - "+(exitList == null ? -1 : exitList.size()) + " exit links"; + if (exitList != null && !exitList.isEmpty()) { + for (MetadataGraphEdge e : graph.getExcidentEdges(from)) { + visit(e.getTarget(), visited, graph); + } + } + } + + // ------------------------------------------------------------------------------------- + private MetadataGraphEdge cleanEdges( + MetadataGraphVertex v, List edges, ArtifactScopeEnum scope) { + if (edges == null || edges.isEmpty()) { + return null; + } + + if (edges.size() == 1) { + MetadataGraphEdge e = edges.get(0); + if (scope.encloses(e.getScope())) { + return e; + } + + return null; + } + + MetadataGraphEdge res = null; + + for (MetadataGraphEdge e : edges) { + if (!scope.encloses(e.getScope())) { + continue; + } + + if (res == null) { + res = e; + } else { + res = policy.apply(e, res); + } + } + + return res; + } + // ------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------- +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionException.java new file mode 100644 index 000000000000..68a22c99a45d --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionException.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +/** + * + * + */ +@Deprecated +public class GraphConflictResolutionException extends Exception { + private static final long serialVersionUID = 2677613140287940255L; + + public GraphConflictResolutionException() {} + + public GraphConflictResolutionException(String message) { + super(message); + } + + public GraphConflictResolutionException(Throwable cause) { + super(cause); + } + + public GraphConflictResolutionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionPolicy.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionPolicy.java similarity index 80% rename from maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionPolicy.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionPolicy.java index 979d3831ff64..0662dfffed55 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionPolicy.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolutionPolicy.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.metadata; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,18 +16,17 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.metadata; /** * MetadataGraph edge selection policy. Complements * GraphConflictResolver by being injected into it * - * @author Oleg Gusakov * */ - -public interface GraphConflictResolutionPolicy -{ +@Deprecated +public interface GraphConflictResolutionPolicy { String ROLE = GraphConflictResolutionPolicy.class.getName(); - MetadataGraphEdge apply( MetadataGraphEdge e1, MetadataGraphEdge e2 ); + MetadataGraphEdge apply(MetadataGraphEdge e1, MetadataGraphEdge e2); } diff --git a/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolver.java similarity index 84% rename from maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolver.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolver.java index ef70baa24cda..e573f82fe0a3 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolver.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/GraphConflictResolver.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.metadata; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.metadata; import org.apache.maven.artifact.ArtifactScopeEnum; @@ -25,10 +24,9 @@ * Resolves conflicts in the supplied dependency graph. * Different implementations will implement different conflict resolution policies. * - * @author Oleg Gusakov */ -public interface GraphConflictResolver -{ +@Deprecated +public interface GraphConflictResolver { String ROLE = GraphConflictResolver.class.getName(); /** @@ -43,6 +41,6 @@ public interface GraphConflictResolver * * @since 3.0 */ - MetadataGraph resolveConflicts( MetadataGraph graph, ArtifactScopeEnum scope ) - throws GraphConflictResolutionException; + MetadataGraph resolveConflicts(MetadataGraph graph, ArtifactScopeEnum scope) + throws GraphConflictResolutionException; } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java new file mode 100644 index 000000000000..fe785515a13e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java @@ -0,0 +1,433 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; + +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * maven dependency metadata graph + * + * + */ +@Deprecated +public class MetadataGraph { + public static final int DEFAULT_VERTICES = 32; + public static final int DEFAULT_EDGES = 64; + + // flags to indicate the granularity of vertices + private boolean versionedVertices = false; + private boolean scopedVertices = false; + /** + * the entry point we started building the graph from + */ + MetadataGraphVertex entry; + + // graph vertices + TreeSet vertices; + + /** + * incident and excident edges per node + */ + Map> incidentEdges; + + Map> excidentEdges; + + /** + * null in dirty graph, actual + * scope for conflict-resolved graph + */ + ArtifactScopeEnum scope; + + // ------------------------------------------------------------------------ + /** + * init graph + */ + public MetadataGraph(int nVertices) { + init(nVertices, 2 * nVertices); + } + + public MetadataGraph(int nVertices, int nEdges) { + init(nVertices, nEdges); + } + // ------------------------------------------------------------------------ + /** + * construct a single vertex + */ + public MetadataGraph(MetadataGraphVertex entry) throws MetadataResolutionException { + checkVertex(entry); + checkVertices(1); + + entry.setCompareVersion(versionedVertices); + entry.setCompareScope(scopedVertices); + + vertices.add(entry); + this.entry = entry; + } + // ------------------------------------------------------------------------ + /** + * construct graph from a "dirty" tree + */ + public MetadataGraph(MetadataTreeNode tree) throws MetadataResolutionException { + this(tree, false, false); + } + // ------------------------------------------------------------------------ + /** + * construct graph from a "dirty" tree + * + * @param tree "dirty" tree root + * @param versionedVertices true if graph nodes should be versioned (different versions -> different nodes) + * @param scopedVertices true if graph nodes should be versioned and scoped + * (different versions and/or scopes -> different nodes) + * + */ + public MetadataGraph(MetadataTreeNode tree, boolean versionedVertices, boolean scopedVertices) + throws MetadataResolutionException { + if (tree == null) { + throw new MetadataResolutionException("tree is null"); + } + + setVersionedVertices(versionedVertices); + setScopedVertices(scopedVertices); + + this.versionedVertices = scopedVertices || versionedVertices; + this.scopedVertices = scopedVertices; + + int count = countNodes(tree); + + init(count, count + (count / 2)); + + processTreeNodes(null, tree, 0, 0); + } + // ------------------------------------------------------------------------ + private void processTreeNodes(MetadataGraphVertex parentVertex, MetadataTreeNode node, int depth, int pomOrder) + throws MetadataResolutionException { + if (node == null) { + return; + } + + MetadataGraphVertex vertex = new MetadataGraphVertex(node.md, versionedVertices, scopedVertices); + vertices.add(vertex); + + if (parentVertex != null) // then create the edge + { + ArtifactMetadata md = node.getMd(); + MetadataGraphEdge e = + new MetadataGraphEdge(md.version, md.resolved, md.artifactScope, md.artifactUri, depth, pomOrder); + addEdge(parentVertex, vertex, e); + } else { + entry = vertex; + } + + MetadataTreeNode[] kids = node.getChildren(); + if (kids == null || kids.length < 1) { + return; + } + + for (int i = 0; i < kids.length; i++) { + MetadataTreeNode n = kids[i]; + processTreeNodes(vertex, n, depth + 1, i); + } + } + // ------------------------------------------------------------------------ + public MetadataGraphVertex findVertex(ArtifactMetadata md) { + if (md == null || vertices == null || vertices.isEmpty()) { + return null; + } + + MetadataGraphVertex v = new MetadataGraphVertex(md); + v.setCompareVersion(versionedVertices); + v.setCompareScope(scopedVertices); + + for (MetadataGraphVertex gv : vertices) { + if (gv.equals(v)) { + return gv; + } + } + + return null; + } + // ------------------------------------------------------------------------ + public MetadataGraphVertex addVertex(ArtifactMetadata md) { + if (md == null) { + return null; + } + + checkVertices(); + + MetadataGraphVertex v = findVertex(md); + if (v != null) { + return v; + } + + v = new MetadataGraphVertex(md); + + v.setCompareVersion(versionedVertices); + v.setCompareScope(scopedVertices); + + vertices.add(v); + return v; + } + // ------------------------------------------------------------------------ + /** + * init graph + */ + private void init(int nVertices, int nEdges) { + int nV = nVertices; + if (nVertices < 1) { + nV = 1; + } + + checkVertices(nV); + + int nE = nVertices; + if (nEdges <= nV) { + nE = 2 * nE; + } + + checkEdges(nE); + } + + private void checkVertices() { + checkVertices(DEFAULT_VERTICES); + } + + private void checkVertices(int nVertices) { + if (vertices == null) { + vertices = new TreeSet<>(); + } + } + + private void checkEdges() { + int count = DEFAULT_EDGES; + + if (vertices != null) { + count = vertices.size() + vertices.size() / 2; + } + + checkEdges(count); + } + + private void checkEdges(int nEdges) { + if (incidentEdges == null) { + incidentEdges = new HashMap<>(nEdges); + } + if (excidentEdges == null) { + excidentEdges = new HashMap<>(nEdges); + } + } + // ------------------------------------------------------------------------ + private static void checkVertex(MetadataGraphVertex v) throws MetadataResolutionException { + if (v == null) { + throw new MetadataResolutionException("null vertex"); + } + if (v.getMd() == null) { + throw new MetadataResolutionException("vertex without metadata"); + } + } + // ------------------------------------------------------------------------ + private static void checkEdge(MetadataGraphEdge e) throws MetadataResolutionException { + if (e == null) { + throw new MetadataResolutionException("badly formed edge"); + } + } + // ------------------------------------------------------------------------ + public List getEdgesBetween(MetadataGraphVertex vFrom, MetadataGraphVertex vTo) { + List edges = getIncidentEdges(vTo); + if (edges == null || edges.isEmpty()) { + return null; + } + + List res = new ArrayList<>(edges.size()); + + for (MetadataGraphEdge e : edges) { + if (e.getSource().equals(vFrom)) { + res.add(e); + } + } + + return res; + } + // ------------------------------------------------------------------------ + public MetadataGraph addEdge(MetadataGraphVertex vFrom, MetadataGraphVertex vTo, MetadataGraphEdge e) + throws MetadataResolutionException { + checkVertex(vFrom); + checkVertex(vTo); + + checkVertices(); + + checkEdge(e); + checkEdges(); + + e.setSource(vFrom); + e.setTarget(vTo); + + vFrom.setCompareVersion(versionedVertices); + vFrom.setCompareScope(scopedVertices); + + List exList = excidentEdges.computeIfAbsent(vFrom, k -> new ArrayList<>()); + + if (!exList.contains(e)) { + exList.add(e); + } + + List inList = incidentEdges.computeIfAbsent(vTo, k -> new ArrayList<>()); + + if (!inList.contains(e)) { + inList.add(e); + } + + return this; + } + // ------------------------------------------------------------------------ + public MetadataGraph removeVertex(MetadataGraphVertex v) { + if (vertices != null && v != null) { + vertices.remove(v); + } + + if (incidentEdges != null) { + incidentEdges.remove(v); + } + + if (excidentEdges != null) { + excidentEdges.remove(v); + } + + return this; + } + // ------------------------------------------------------------------------ + private static int countNodes(MetadataTreeNode tree) { + if (tree == null) { + return 0; + } + + int count = 1; + MetadataTreeNode[] kids = tree.getChildren(); + if (kids == null || kids.length < 1) { + return count; + } + for (MetadataTreeNode n : kids) { + count += countNodes(n); + } + + return count; + } + + // ------------------------------------------------------------------------ + public MetadataGraphVertex getEntry() { + return entry; + } + + public void setEntry(MetadataGraphVertex entry) { + this.entry = entry; + } + + public TreeSet getVertices() { + return vertices; + } + + public List getIncidentEdges(MetadataGraphVertex vertex) { + checkEdges(); + return incidentEdges.get(vertex); + } + + public List getExcidentEdges(MetadataGraphVertex vertex) { + checkEdges(); + return excidentEdges.get(vertex); + } + + public boolean isVersionedVertices() { + return versionedVertices; + } + + public void setVersionedVertices(boolean versionedVertices) { + this.versionedVertices = versionedVertices; + } + + public boolean isScopedVertices() { + return scopedVertices; + } + + public void setScopedVertices(boolean scopedVertices) { + this.scopedVertices = scopedVertices; + + // scoped graph is versioned by definition + if (scopedVertices) { + versionedVertices = true; + } + } + + public ArtifactScopeEnum getScope() { + return scope; + } + + public void setScope(ArtifactScopeEnum scope) { + this.scope = scope; + } + + // ------------------------------------------------------------------------ + public boolean isEmpty() { + return entry == null || vertices == null || vertices.isEmpty(); + } + + // ------------------------------------------------------------------------ + public boolean isEmptyEdges() { + return isEmpty() || incidentEdges == null || incidentEdges.isEmpty(); + } + // ------------------------------------------------------------------------ + @Override + public String toString() { + if (isEmpty()) { + return "empty"; + } + StringBuilder sb = new StringBuilder(512); + for (MetadataGraphVertex v : vertices) { + sb.append("Vertex: ").append(v.getMd().toString()).append('\n'); + List ins = getIncidentEdges(v); + if (ins != null) { + for (MetadataGraphEdge e : ins) { + sb.append(" from : ").append(e.toString()).append('\n'); + } + } else { + sb.append(" no entries\n"); + } + + List outs = getExcidentEdges(v); + if (outs != null) { + for (MetadataGraphEdge e : outs) { + sb.append(" to : ").append(e.toString()).append('\n'); + } + } else { + sb.append(" no exit\n"); + } + + sb.append("-------------------------------------------------\n"); + } + sb.append("=============================================================\n"); + return sb.toString(); + } + + // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------ +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphEdge.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphEdge.java new file mode 100644 index 000000000000..a42efd5c678f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphEdge.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * metadata graph edge - combination of version, scope and depth define + * an edge in the graph + * + * + */ +@Deprecated +public class MetadataGraphEdge { + String version; + ArtifactScopeEnum scope; + int depth = -1; + int pomOrder = -1; + boolean resolved = true; + String artifactUri; + + /** + * capturing where this link came from + * and where it is linked to. + * + * In the first implementation only source used for explanatory function + */ + MetadataGraphVertex source; + + MetadataGraphVertex target; + + // ---------------------------------------------------------------------------- + public MetadataGraphEdge( + String version, boolean resolved, ArtifactScopeEnum scope, String artifactUri, int depth, int pomOrder) { + super(); + this.version = version; + this.scope = scope; + this.artifactUri = artifactUri; + this.depth = depth; + this.resolved = resolved; + this.pomOrder = pomOrder; + } + // ---------------------------------------------------------------------------- + /** + * helper for equals + */ + private static boolean objectsEqual(Object o1, Object o2) { + if (o1 == null && o2 == null) { + return true; + } + if (o1 == null || o2 == null) { + return false; // as they are not both null + } + return o1.equals(o2); + } + + // ---------------------------------------------------------------------------- + /** + * used to eliminate exact duplicates in the edge list + */ + @Override + @SuppressWarnings("checkstyle:equalshashcode") + public boolean equals(Object o) { + if (o instanceof MetadataGraphEdge e) { + return objectsEqual(version, e.version) + && ArtifactScopeEnum.checkScope(scope) + .getScope() + .equals(ArtifactScopeEnum.checkScope(e.scope).getScope()) + && depth == e.depth; + } + return false; + } + + // ---------------------------------------------------------------------------- + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public ArtifactScopeEnum getScope() { + return scope; + } + + public void setScope(ArtifactScopeEnum scope) { + this.scope = scope; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public boolean isResolved() { + return resolved; + } + + public void setResolved(boolean resolved) { + this.resolved = resolved; + } + + public int getPomOrder() { + return pomOrder; + } + + public void setPomOrder(int pomOrder) { + this.pomOrder = pomOrder; + } + + public String getArtifactUri() { + return artifactUri; + } + + public void setArtifactUri(String artifactUri) { + this.artifactUri = artifactUri; + } + + public MetadataGraphVertex getSource() { + return source; + } + + public void setSource(MetadataGraphVertex source) { + this.source = source; + } + + public MetadataGraphVertex getTarget() { + return target; + } + + public void setTarget(MetadataGraphVertex target) { + this.target = target; + } + + @Override + public String toString() { + return "[ " + "FROM:(" + + (source == null ? "no source" : (source.md == null ? "no source MD" : source.md.toString())) + ") " + + "TO:(" + (target == null ? "no target" : (target.md == null ? "no target MD" : target.md.toString())) + + ") " + "version=" + version + ", scope=" + (scope == null ? "null" : scope.getScope()) + ", depth=" + + depth + "]"; + } + // ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphTransformationException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphTransformationException.java new file mode 100644 index 000000000000..85e79fef60d0 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphTransformationException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +/** + */ +@Deprecated +public class MetadataGraphTransformationException extends Exception { + + private static final long serialVersionUID = -4029897098314019152L; + + public MetadataGraphTransformationException() {} + + public MetadataGraphTransformationException(String message) { + super(message); + } + + public MetadataGraphTransformationException(Throwable cause) { + super(cause); + } + + public MetadataGraphTransformationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphVertex.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphVertex.java new file mode 100644 index 000000000000..a7eb1c0ef61f --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraphVertex.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * metadata graph vertice - just a wrapper around artifact's metadata + * + */ +@Deprecated +public class MetadataGraphVertex implements Comparable { + ArtifactMetadata md; + + // indications to use these in comparison + private boolean compareVersion = false; + private boolean compareScope = false; + + public MetadataGraphVertex(ArtifactMetadata md) { + super(); + this.md = md; + } + + public MetadataGraphVertex(ArtifactMetadata md, boolean compareVersion, boolean compareScope) { + this(md); + this.compareVersion = compareVersion; + this.compareScope = compareScope; + } + + public ArtifactMetadata getMd() { + return md; + } + + public void setMd(ArtifactMetadata md) { + this.md = md; + } + + // --------------------------------------------------------------------- + public boolean isCompareVersion() { + return compareVersion; + } + + public void setCompareVersion(boolean compareVersion) { + this.compareVersion = compareVersion; + } + + public boolean isCompareScope() { + return compareScope; + } + + public void setCompareScope(boolean compareScope) { + this.compareScope = compareScope; + } + + // --------------------------------------------------------------------- + @Override + public String toString() { + return "[" + (md == null ? "no metadata" : md.toString()) + "]"; + } + + // --------------------------------------------------------------------- + private static int compareStrings(String s1, String s2) { + if (s1 == null && s2 == null) { + return 0; + } + + if (s1 == null /* && s2 != null */) { + return -1; + } + + if ( + /* s1 != null && */ s2 == null) { + return 1; + } + + return s1.compareTo(s2); + } + + // --------------------------------------------------------------------- + @Override + public int compareTo(MetadataGraphVertex vertex) { + if (vertex == null || vertex.getMd() == null) { + return 1; + } + + ArtifactMetadata vmd = vertex.getMd(); + + if (vmd == null) { + if (md == null) { + return 0; + } else { + return 1; + } + } + + int g = compareStrings(md.groupId, vmd.groupId); + + if (g == 0) { + int a = compareStrings(md.artifactId, vmd.artifactId); + if (a == 0) { + if (compareVersion) { + int v = compareStrings(md.version, vmd.version); + if (v == 0) { + if (compareScope) { + String s1 = ArtifactScopeEnum.checkScope(md.artifactScope) + .getScope(); + String s2 = ArtifactScopeEnum.checkScope(vmd.artifactScope) + .getScope(); + return s1.compareTo(s2); + } else { + return 0; + } + } else { + return v; + } + } else { + return 0; + } + } else { + return a; + } + } + + return g; + } + + // --------------------------------------------------------------------- + @Override + public boolean equals(Object vo) { + if (vo instanceof MetadataGraphVertex metadataGraphVertex) { + return compareTo(metadataGraphVertex) == 0; + } + return false; + } + + // --------------------------------------------------------------------- + + @Override + public int hashCode() { + if (md == null) { + return super.hashCode(); + } + StringBuilder hashString = new StringBuilder(128); + hashString.append(md.groupId).append('|'); + hashString.append(md.artifactId).append('|'); + + if (compareVersion) { + hashString.append(md.version).append('|'); + } + + if (compareScope) { + hashString.append(md.getArtifactScope()).append('|'); + } + + return hashString.toString().hashCode(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolution.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolution.java new file mode 100644 index 000000000000..21d48366bdf9 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolution.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import java.util.Collection; + +import org.apache.maven.artifact.repository.ArtifactRepository; + +/** + * + * + */ +@Deprecated +public class MetadataResolution { + /** resolved MD */ + private ArtifactMetadata artifactMetadata; + + /** repositories, added by this POM */ + private Collection metadataRepositories; + // ------------------------------------------------------------------- + public MetadataResolution(ArtifactMetadata artifactMetadata) { + this.artifactMetadata = artifactMetadata; + } + // ------------------------------------------------------------------- + public MetadataResolution(ArtifactMetadata artifactMetadata, Collection metadataRepositories) { + this(artifactMetadata); + this.metadataRepositories = metadataRepositories; + } + // ------------------------------------------------------------------- + public Collection getMetadataRepositories() { + return metadataRepositories; + } + + public void setMetadataRepositories(Collection metadataRepositories) { + this.metadataRepositories = metadataRepositories; + } + // ------------------------------------------------------------------- + public ArtifactMetadata getArtifactMetadata() { + return artifactMetadata; + } + + public void setArtifactMetadata(ArtifactMetadata artifactMetadata) { + this.artifactMetadata = artifactMetadata; + } + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionException.java new file mode 100644 index 000000000000..ab18144c17c4 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionException.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +/** + * MetadataResolutionException + */ +@Deprecated +public class MetadataResolutionException extends Exception { + + public MetadataResolutionException() { + // TODO Auto-generated constructor stub + } + + public MetadataResolutionException(String message) { + super(message); + // TODO Auto-generated constructor stub + } + + public MetadataResolutionException(Throwable cause) { + super(cause); + // TODO Auto-generated constructor stub + } + + public MetadataResolutionException(String message, Throwable cause) { + super(message, cause); + // TODO Auto-generated constructor stub + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionRequest.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionRequest.java new file mode 100644 index 000000000000..ec2bbecd6dee --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionRequest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import java.util.List; + +import org.apache.maven.artifact.repository.ArtifactRepository; + +@Deprecated +public class MetadataResolutionRequest { + protected ArtifactMetadata query; + protected ArtifactRepository localRepository; + protected List remoteRepositories; + + // -------------------------------------------------------------------- + public MetadataResolutionRequest() {} + + // -------------------------------------------------------------------- + public MetadataResolutionRequest( + ArtifactMetadata query, ArtifactRepository localRepository, List remoteRepositories) { + this.query = query; + this.localRepository = localRepository; + this.remoteRepositories = remoteRepositories; + } + + // -------------------------------------------------------------------- + public ArtifactMetadata getQuery() { + return query; + } + + public void setQuery(ArtifactMetadata query) { + this.query = query; + } + + public ArtifactRepository getLocalRepository() { + return localRepository; + } + + public void setLocalRepository(ArtifactRepository localRepository) { + this.localRepository = localRepository; + } + + public List getRemoteRepositories() { + return remoteRepositories; + } + + public void setRemoteRepositories(List remoteRepositories) { + this.remoteRepositories = remoteRepositories; + } + // -------------------------------------------------------------------- + // -------------------------------------------------------------------- +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionRequestTypeEnum.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionRequestTypeEnum.java new file mode 100644 index 000000000000..927464b7c46c --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionRequestTypeEnum.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +/** + * MetadataResolutionRequestTypeEnum + */ +@Deprecated +public enum MetadataResolutionRequestTypeEnum { + tree(1), + graph(2), + classpathCompile(3), + classpathTest(4), + classpathRuntime(5), + versionedGraph(6), + scopedGraph(7); + + private int id; + + // Constructor + MetadataResolutionRequestTypeEnum(int id) { + this.id = id; + } + + int getId() { + return id; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionResult.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionResult.java new file mode 100644 index 000000000000..e5bd8f14ed65 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataResolutionResult.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import org.apache.maven.artifact.ArtifactScopeEnum; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +/** + * This object is tinted with ClasspathTransformation and GraphConflictResolver. + * Get rid of them after debugging + * + */ +@Deprecated +public class MetadataResolutionResult { + MetadataTreeNode treeRoot; + + /** + * these components are initialized on demand by + * explicit call of the initTreeProcessing() + */ + ClasspathTransformation classpathTransformation; + + GraphConflictResolver conflictResolver; + + // ---------------------------------------------------------------------------- + public MetadataResolutionResult() {} + // ---------------------------------------------------------------------------- + public MetadataResolutionResult(MetadataTreeNode root) { + this.treeRoot = root; + } + // ---------------------------------------------------------------------------- + public MetadataTreeNode getTree() { + return treeRoot; + } + // ---------------------------------------------------------------------------- + public void setTree(MetadataTreeNode root) { + this.treeRoot = root; + } + + public void initTreeProcessing(PlexusContainer plexus) throws ComponentLookupException { + classpathTransformation = plexus.lookup(ClasspathTransformation.class); + conflictResolver = plexus.lookup(GraphConflictResolver.class); + } + // ---------------------------------------------------------------------------- + public MetadataGraph getGraph() throws MetadataResolutionException { + return treeRoot == null ? null : new MetadataGraph(treeRoot); + } + // ---------------------------------------------------------------------------- + public MetadataGraph getGraph(ArtifactScopeEnum scope) + throws MetadataResolutionException, GraphConflictResolutionException { + if (treeRoot == null) { + return null; + } + + if (conflictResolver == null) { + return null; + } + + return conflictResolver.resolveConflicts(getGraph(), scope); + } + // ---------------------------------------------------------------------------- + public MetadataGraph getGraph(MetadataResolutionRequestTypeEnum requestType) + throws MetadataResolutionException, GraphConflictResolutionException { + if (requestType == null) { + return null; + } + + if (treeRoot == null) { + return null; + } + + if (conflictResolver == null) { + return null; + } + + if (requestType.equals(MetadataResolutionRequestTypeEnum.classpathCompile)) { + return conflictResolver.resolveConflicts(getGraph(), ArtifactScopeEnum.compile); + } else if (requestType.equals(MetadataResolutionRequestTypeEnum.classpathRuntime)) { + return conflictResolver.resolveConflicts(getGraph(), ArtifactScopeEnum.runtime); + } else if (requestType.equals(MetadataResolutionRequestTypeEnum.classpathTest)) { + return conflictResolver.resolveConflicts(getGraph(), ArtifactScopeEnum.test); + } else if (requestType.equals(MetadataResolutionRequestTypeEnum.graph)) { + return getGraph(); + } else if (requestType.equals(MetadataResolutionRequestTypeEnum.versionedGraph)) { + return new MetadataGraph(getTree(), true, false); + } else if (requestType.equals(MetadataResolutionRequestTypeEnum.scopedGraph)) { + return new MetadataGraph(getTree(), true, true); + } + return null; + } + // ---------------------------------------------------------------------------- + public ClasspathContainer getClasspath(ArtifactScopeEnum scope) + throws MetadataGraphTransformationException, MetadataResolutionException { + if (classpathTransformation == null) { + return null; + } + + MetadataGraph dirtyGraph = getGraph(); + if (dirtyGraph == null) { + return null; + } + + return classpathTransformation.transform(dirtyGraph, scope, false); + } + + // ---------------------------------------------------------------------------- + public MetadataTreeNode getClasspathTree(ArtifactScopeEnum scope) + throws MetadataGraphTransformationException, MetadataResolutionException { + ClasspathContainer cpc = getClasspath(scope); + if (cpc == null) { + return null; + } + + return cpc.getClasspathAsTree(); + } + // ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataRetrievalException.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataRetrievalException.java new file mode 100644 index 000000000000..dd7d51ef54cd --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataRetrievalException.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +/** + * Error while retrieving repository metadata from the repository. + * + */ +@Deprecated +public class MetadataRetrievalException extends Exception { + + private ArtifactMetadata artifact; + + public MetadataRetrievalException(String message) { + this(message, null, null); + } + + public MetadataRetrievalException(Throwable cause) { + this(null, cause, null); + } + + public MetadataRetrievalException(String message, Throwable cause) { + this(message, cause, null); + } + + public MetadataRetrievalException(String message, Throwable cause, ArtifactMetadata artifact) { + super(message, cause); + + this.artifact = artifact; + } + + public ArtifactMetadata getArtifactMetadata() { + return artifact; + } +} diff --git a/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataSource.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataSource.java similarity index 76% rename from maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataSource.java rename to compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataSource.java index 3ca6ce84f54d..bab22f70000d 100644 --- a/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataSource.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataSource.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.metadata; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.metadata; import java.util.List; @@ -27,13 +26,12 @@ * Provides some metadata operations, like querying the remote repository for a list of versions available for an * artifact. * - * @author Jason van Zyl */ -public interface MetadataSource -{ +@Deprecated +public interface MetadataSource { String ROLE = MetadataSource.class.getName(); - MetadataResolution retrieve( ArtifactMetadata artifact, ArtifactRepository localRepository, - List remoteRepositories ) - throws MetadataRetrievalException; -} \ No newline at end of file + MetadataResolution retrieve( + ArtifactMetadata artifact, ArtifactRepository localRepository, List remoteRepositories) + throws MetadataRetrievalException; +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataTreeNode.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataTreeNode.java new file mode 100644 index 000000000000..7fbe882ad759 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataTreeNode.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactScopeEnum; + +/** + * metadata [dirty] Tree + * + * + */ +@Deprecated +public class MetadataTreeNode { + ArtifactMetadata md; // this node + + MetadataTreeNode parent; // papa + + /** default # of children. Used for tree creation optimization only */ + int nChildren = 8; + + MetadataTreeNode[] children; // of cause + + public int getNChildren() { + return nChildren; + } + + public void setNChildren(int children) { + nChildren = children; + } + + // ------------------------------------------------------------------------ + public MetadataTreeNode() {} + // ------------------------------------------------------------------------ + public MetadataTreeNode(ArtifactMetadata md, MetadataTreeNode parent, boolean resolved, ArtifactScopeEnum scope) { + if (md != null) { + md.setArtifactScope(ArtifactScopeEnum.checkScope(scope)); + md.setResolved(resolved); + } + + this.md = md; + this.parent = parent; + } + // ------------------------------------------------------------------------ + public MetadataTreeNode(Artifact af, MetadataTreeNode parent, boolean resolved, ArtifactScopeEnum scope) { + this(new ArtifactMetadata(af), parent, resolved, scope); + } + + // ------------------------------------------------------------------------ + public void addChild(int index, MetadataTreeNode kid) { + if (kid == null) { + return; + } + + if (children == null) { + children = new MetadataTreeNode[nChildren]; + } + + children[index % nChildren] = kid; + } + + // ------------------------------------------------------------------ + @Override + public String toString() { + return md == null ? "no metadata" : md.toString(); + } + + // ------------------------------------------------------------------ + public String graphHash() throws MetadataResolutionException { + if (md == null) { + throw new MetadataResolutionException( + "treenode without metadata, parent: " + (parent == null ? "null" : parent.toString())); + } + + return md.groupId + ":" + md.artifactId; + } + + // ------------------------------------------------------------------------ + public boolean hasChildren() { + return children != null; + } + // ------------------------------------------------------------------------ + public ArtifactMetadata getMd() { + return md; + } + + public void setMd(ArtifactMetadata md) { + this.md = md; + } + + public MetadataTreeNode getParent() { + return parent; + } + + public void setParent(MetadataTreeNode parent) { + this.parent = parent; + } + + public MetadataTreeNode[] getChildren() { + return children; + } + + public void setChildren(MetadataTreeNode[] children) { + this.children = children; + } + // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------ + +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/settings/DefaultMavenSettingsBuilder.java b/compat/maven-compat/src/main/java/org/apache/maven/settings/DefaultMavenSettingsBuilder.java new file mode 100644 index 000000000000..ec819401cd7e --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/settings/DefaultMavenSettingsBuilder.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.settings; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.properties.internal.SystemProperties; +import org.apache.maven.settings.building.DefaultSettingsBuildingRequest; +import org.apache.maven.settings.building.SettingsBuilder; +import org.apache.maven.settings.building.SettingsBuildingException; +import org.apache.maven.settings.building.SettingsBuildingRequest; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + */ +@Deprecated +@Named +@Singleton +public class DefaultMavenSettingsBuilder extends AbstractLogEnabled implements MavenSettingsBuilder { + + private final SettingsBuilder settingsBuilder; + + @Inject + public DefaultMavenSettingsBuilder(SettingsBuilder settingsBuilder) { + this.settingsBuilder = settingsBuilder; + } + + @Override + public Settings buildSettings() throws IOException, XmlPullParserException { + File userSettingsFile = getFile( + "${user.home}/.m2/settings.xml", "user.home", MavenSettingsBuilder.ALT_USER_SETTINGS_XML_LOCATION); + + return buildSettings(userSettingsFile); + } + + @Override + public Settings buildSettings(boolean useCachedSettings) throws IOException, XmlPullParserException { + return buildSettings(); + } + + @Override + public Settings buildSettings(File userSettingsFile) throws IOException, XmlPullParserException { + File globalSettingsFile = getFile( + "${maven.conf}/settings.xml", "maven.conf", MavenSettingsBuilder.ALT_GLOBAL_SETTINGS_XML_LOCATION); + + SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); + request.setUserSettingsFile(userSettingsFile); + request.setGlobalSettingsFile(globalSettingsFile); + request.setSystemProperties(SystemProperties.getSystemProperties()); + return build(request); + } + + @Override + public Settings buildSettings(File userSettingsFile, boolean useCachedSettings) + throws IOException, XmlPullParserException { + return buildSettings(userSettingsFile); + } + + private Settings build(SettingsBuildingRequest request) throws IOException, XmlPullParserException { + try { + return settingsBuilder.build(request).getEffectiveSettings(); + } catch (SettingsBuildingException e) { + throw new IOException(e.getMessage(), e); + } + } + + /** @since 2.1 */ + @Override + public Settings buildSettings(MavenExecutionRequest request) throws IOException, XmlPullParserException { + SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest(); + settingsRequest.setUserSettingsFile(request.getUserSettingsFile()); + settingsRequest.setProjectSettingsFile(request.getProjectSettingsFile()); + settingsRequest.setGlobalSettingsFile(request.getGlobalSettingsFile()); + settingsRequest.setUserProperties(request.getUserProperties()); + settingsRequest.setSystemProperties(request.getSystemProperties()); + + return build(settingsRequest); + } + + private File getFile(String pathPattern, String basedirSysProp, String altLocationSysProp) { + // ------------------------------------------------------------------------------------- + // Alright, here's the justification for all the regexp wizardry below... + // + // Continuum and other server-like apps may need to locate the user-level and + // global-level settings somewhere other than ${user.home} and ${maven.home}, + // respectively. Using a simple replacement of these patterns will allow them + // to specify the absolute path to these files in a customized components.xml + // file. Ideally, we'd do full pattern-evaluation against the sysprops, but this + // is a first step. There are several replacements below, in order to normalize + // the path character before we operate on the string as a regex input, and + // in order to avoid surprises with the File construction... + // ------------------------------------------------------------------------------------- + + String path = System.getProperty(altLocationSysProp); + + if (path == null || path.isEmpty()) { + // TODO This replacing shouldn't be necessary as user.home should be in the + // context of the container and thus the value would be interpolated by Plexus + String basedir = System.getProperty(basedirSysProp); + if (basedir == null) { + basedir = System.getProperty("user.dir"); + } + + basedir = basedir.replace("\\", "/"); + basedir = basedir.replace("$", "\\$"); + + // basedirSysProp is non regexp and basedir too + path = pathPattern.replace("${" + basedirSysProp + "}", basedir); + path = path.replace("\\", "/"); + } + return new File(path).getAbsoluteFile(); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/settings/MavenSettingsBuilder.java b/compat/maven-compat/src/main/java/org/apache/maven/settings/MavenSettingsBuilder.java similarity index 76% rename from maven-core/src/main/java/org/apache/maven/settings/MavenSettingsBuilder.java rename to compat/maven-compat/src/main/java/org/apache/maven/settings/MavenSettingsBuilder.java index c79a843d91a6..ea39c20e507b 100644 --- a/maven-core/src/main/java/org/apache/maven/settings/MavenSettingsBuilder.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/settings/MavenSettingsBuilder.java @@ -1,5 +1,3 @@ -package org.apache.maven.settings; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.settings; import java.io.File; import java.io.IOException; @@ -26,12 +25,9 @@ import org.codehaus.plexus.util.xml.pull.XmlPullParserException; /** - * @author jdcasey - * @author Jason van Zyl */ @Deprecated -public interface MavenSettingsBuilder -{ +public interface MavenSettingsBuilder { String ROLE = MavenSettingsBuilder.class.getName(); @@ -39,16 +35,14 @@ public interface MavenSettingsBuilder String ALT_GLOBAL_SETTINGS_XML_LOCATION = "org.apache.maven.global-settings"; String ALT_LOCAL_REPOSITORY_LOCATION = "maven.repo.local"; - Settings buildSettings( MavenExecutionRequest request ) - throws IOException, XmlPullParserException; + Settings buildSettings(MavenExecutionRequest request) throws IOException, XmlPullParserException; /** * @return a Settings object from the user settings file. * @throws IOException if any * @throws XmlPullParserException if any */ - Settings buildSettings() - throws IOException, XmlPullParserException; + Settings buildSettings() throws IOException, XmlPullParserException; /** * @param useCachedSettings if true, doesn't reload the user settings @@ -56,8 +50,7 @@ Settings buildSettings() * @throws IOException if any * @throws XmlPullParserException if any */ - Settings buildSettings( boolean useCachedSettings ) - throws IOException, XmlPullParserException; + Settings buildSettings(boolean useCachedSettings) throws IOException, XmlPullParserException; /** * @param userSettingsFile a given user settings file @@ -65,8 +58,7 @@ Settings buildSettings( boolean useCachedSettings ) * @throws IOException if any * @throws XmlPullParserException if any */ - Settings buildSettings( File userSettingsFile ) - throws IOException, XmlPullParserException; + Settings buildSettings(File userSettingsFile) throws IOException, XmlPullParserException; /** * @param userSettingsFile a given user settings file @@ -75,7 +67,5 @@ Settings buildSettings( File userSettingsFile ) * @throws IOException if any * @throws XmlPullParserException if any */ - Settings buildSettings( File userSettingsFile, boolean useCachedSettings ) - throws IOException, XmlPullParserException; - + Settings buildSettings(File userSettingsFile, boolean useCachedSettings) throws IOException, XmlPullParserException; } diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/DefaultToolchain.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/DefaultToolchain.java new file mode 100644 index 000000000000..ef89cee08f20 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/DefaultToolchain.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +import org.apache.maven.toolchain.model.ToolchainModel; +import org.slf4j.Logger; + +/** + * Default abstract toolchain implementation, to be used as base class for any toolchain implementation + * to avoid rewriting usual code. + * + * @since 2.0.9 + * @deprecated Use {@link org.apache.maven.api.Toolchain} instead. + */ +@Deprecated(since = "4.0.0") +public abstract class DefaultToolchain // should have been AbstractToolchain... +implements Toolchain, ToolchainPrivate { + private final Logger logger; + + private String type; + + private Map provides = new HashMap<>(); + + public static final String KEY_TYPE = "type"; // NOI18N + + private ToolchainModel model; + + /** + * + * @param model the model, must not be {@code null} + * @param logger the logger, must not be {@code null} + */ + protected DefaultToolchain(ToolchainModel model, Logger logger) { + this.model = model; + this.logger = logger; + } + + /** + * + * @param model the model, must not be {@code null} + * @param type the type + * @param logger the logger, must not be {@code null} + */ + protected DefaultToolchain(ToolchainModel model, String type, Logger logger) { + this(model, logger); + this.type = type; + } + + @Override + public final String getType() { + return type != null ? type : model.getType(); + } + + @Override + public final ToolchainModel getModel() { + return model; + } + + public final void addProvideToken(String type, RequirementMatcher matcher) { + provides.put(type, matcher); + } + + @Override + public boolean matchesRequirements(Map requirements) { + for (Map.Entry requirement : requirements.entrySet()) { + String key = requirement.getKey(); + + RequirementMatcher matcher = provides.get(key); + + if (matcher == null) { + getLog().debug("Toolchain {} is missing required property: {}", this, key); + return false; + } + if (!matcher.matches(requirement.getValue())) { + getLog().debug("Toolchain {} doesn't match required property: {}", this, key); + return false; + } + } + return true; + } + + protected Logger getLog() { + return logger; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (this == obj) { + return true; + } + + if (obj instanceof DefaultToolchain other) { + if (!Objects.equals(type, other.type)) { + return false; + } + + Properties thisProvides = this.getModel().getProvides(); + Properties otherProvides = other.getModel().getProvides(); + + return Objects.equals(thisProvides, otherProvides); + } else { + return false; + } + } + + @Override + public int hashCode() { + int hashCode = (type == null) ? 0 : type.hashCode(); + + if (this.getModel().getProvides() != null) { + hashCode = 31 * hashCode + this.getModel().getProvides().hashCode(); + } + return hashCode; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("type:").append(getType()); + builder.append('{'); + + Iterator> providesIter = + provides.entrySet().iterator(); + while (providesIter.hasNext()) { + Map.Entry provideEntry = providesIter.next(); + builder.append(provideEntry.getKey()).append(" = ").append(provideEntry.getValue()); + if (providesIter.hasNext()) { + builder.append(';'); + } + } + + builder.append('}'); + + return builder.toString(); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/DefaultToolchainsBuilder.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/DefaultToolchainsBuilder.java new file mode 100644 index 000000000000..edb157701781 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/DefaultToolchainsBuilder.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; + +import org.apache.maven.toolchain.model.PersistedToolchains; +import org.apache.maven.toolchain.v4.MavenToolchainsStaxReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @deprecated instead use {@link org.apache.maven.toolchain.building.DefaultToolchainsBuilder} + */ +@Deprecated +@Named("default") +@Singleton +public class DefaultToolchainsBuilder implements ToolchainsBuilder { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public PersistedToolchains build(File userToolchainsFile) throws MisconfiguredToolchainException { + PersistedToolchains toolchains = null; + + if (userToolchainsFile != null && userToolchainsFile.isFile()) { + try (InputStream in = Files.newInputStream(userToolchainsFile.toPath())) { + toolchains = new PersistedToolchains(new MavenToolchainsStaxReader().read(in)); + } catch (Exception e) { + throw new MisconfiguredToolchainException( + "Cannot read toolchains file at " + userToolchainsFile.getAbsolutePath(), e); + } + + } else if (userToolchainsFile != null) { + logger.debug("Toolchains configuration was not found at {}", userToolchainsFile); + } + + return toolchains; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/MisconfiguredToolchainException.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/MisconfiguredToolchainException.java new file mode 100644 index 000000000000..f0cc46dfd7b9 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/MisconfiguredToolchainException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +/** + * + */ +@Deprecated(since = "4.0.0") +public class MisconfiguredToolchainException extends Exception { + + public MisconfiguredToolchainException(String message) { + super(message); + } + + public MisconfiguredToolchainException(String message, Throwable orig) { + super(message, orig); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcher.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/RequirementMatcher.java similarity index 83% rename from maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcher.java rename to compat/maven-compat/src/main/java/org/apache/maven/toolchain/RequirementMatcher.java index 26390e405c6f..b7008b00cd28 100644 --- a/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcher.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/RequirementMatcher.java @@ -1,5 +1,3 @@ -package org.apache.maven.toolchain; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.toolchain; /** * - * @author mkleint */ -public interface RequirementMatcher -{ +@Deprecated(since = "4.0.0") +public interface RequirementMatcher { - boolean matches( String requirement ); -} \ No newline at end of file + boolean matches(String requirement); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java new file mode 100644 index 000000000000..96344952efdf --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * + */ +@Deprecated(since = "4.0.0") +public final class RequirementMatcherFactory { + private RequirementMatcherFactory() {} + + public static RequirementMatcher createExactMatcher(String provideValue) { + return new ExactMatcher(provideValue); + } + + public static RequirementMatcher createVersionMatcher(String provideValue) { + return new VersionMatcher(provideValue); + } + + private static final class ExactMatcher implements RequirementMatcher { + + private String provides; + + private ExactMatcher(String provides) { + this.provides = provides; + } + + @Override + public boolean matches(String requirement) { + return provides.equalsIgnoreCase(requirement); + } + + @Override + public String toString() { + return provides; + } + } + + private static final class VersionMatcher implements RequirementMatcher { + DefaultArtifactVersion version; + + private VersionMatcher(String version) { + this.version = new DefaultArtifactVersion(version); + } + + @Override + public boolean matches(String requirement) { + try { + VersionRange range = VersionRange.createFromVersionSpec(requirement); + if (range.hasRestrictions()) { + return range.containsVersion(version); + } else { + return range.getRecommendedVersion().compareTo(version) == 0; + } + } catch (InvalidVersionSpecificationException ex) { + return false; + } + } + + @Override + public String toString() { + return version.toString(); + } + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/Toolchain.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/Toolchain.java new file mode 100644 index 000000000000..787df33db2b7 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/Toolchain.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +/** + * Toolchain interface. + * + * @since 2.0.9 + * @deprecated Use {@link org.apache.maven.api.Toolchain} instead. + */ +@Deprecated(since = "4.0.0") +public interface Toolchain { + + /** + * get the type of toolchain. + * + * @return the toolchain type + */ + String getType(); + + /** + * Gets the platform tool executable. + * + * @param toolName the tool platform independent tool name. + * @return file representing the tool executable, or null if the tool can not be found + */ + String findTool(String toolName); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainFactory.java new file mode 100644 index 000000000000..8342cb96ae1a --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainFactory.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import org.apache.maven.toolchain.model.ToolchainModel; + +/** + * Internal toolchain factory, to prepare toolchains instances. + * + * @since 2.0.9 + * @deprecated Use {@link org.apache.maven.api.services.ToolchainFactory} instead. + */ +@Deprecated(since = "4.0.0") +public interface ToolchainFactory { + /** + * Create instance of toolchain. + **/ + ToolchainPrivate createToolchain(ToolchainModel model) throws MisconfiguredToolchainException; + + /** + * Returns the default instance of the particular type of toolchain, can return null + * if not applicable. + * TODO keep around?? + **/ + ToolchainPrivate createDefaultToolchain(); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManager.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManager.java new file mode 100644 index 000000000000..310ebabb5514 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManager.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import java.util.List; +import java.util.Map; + +import org.apache.maven.execution.MavenSession; + +/** + * Public API for a toolchain-aware plugin to get expected toolchain instance. + * + * @since 2.0.9 + * @deprecated Use {@link org.apache.maven.api.services.ToolchainManager} instead. + */ +@Deprecated(since = "4.0.0") +public interface ToolchainManager { + + // NOTE: Some plugins like Surefire access this field directly! + @Deprecated + String ROLE = ToolchainManager.class.getName(); + + /** + * Retrieve toolchain of specified type from build context. It is expected that + * maven-toolchains-plugin contains the configuration to select the appropriate + * toolchain and is executed at the beginning of the build. + * + * @param type the type, must not be {@code null} + * @param context the Maven session, must not be {@code null} + * @return the toolchain selected by maven-toolchains-plugin + */ + Toolchain getToolchainFromBuildContext(String type, MavenSession context); + + /** + * Select all toolchains available in user settings matching the type and requirements, + * independently from maven-toolchains-plugin. + * + * @param session the Maven session, must not be {@code null} + * @param type the type, must not be {@code null} + * @param requirements the requirements, may be {@code null} + * @return the matching toolchains, never {@code null} + * @since 3.3.0 + */ + List getToolchains(MavenSession session, String type, Map requirements); +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManagerFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManagerFactory.java new file mode 100644 index 000000000000..422334a7d0be --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManagerFactory.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.di.Inject; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Priority; +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.di.Singleton; +import org.apache.maven.api.di.Typed; +import org.apache.maven.api.services.Lookup; +import org.apache.maven.api.services.ToolchainFactoryException; +import org.apache.maven.api.services.ToolchainManagerException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.impl.MappedList; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.slf4j.Logger; + +@Named +@Singleton +@Deprecated(since = "4.0.0") +public class ToolchainManagerFactory { + + private final Lookup lookup; + private final Logger logger; + + @Inject + ToolchainManagerFactory(Lookup lookup) { + this(lookup, null); + } + + protected ToolchainManagerFactory(Lookup lookup, Logger logger) { + this.lookup = lookup; + this.logger = logger; + } + + @Provides + @Typed({ToolchainManager.class, ToolchainManagerPrivate.class}) + @Named // qualifier is required for SiduDIBridge to work + DefaultToolchainManagerV3 v3Manager() { + return new DefaultToolchainManagerV3(); + } + + @Provides + @Priority(10) + @Typed(org.apache.maven.api.services.ToolchainManager.class) + DefaultToolchainManagerV4 v4Manager() { + return new DefaultToolchainManagerV4(); + } + + private org.apache.maven.impl.DefaultToolchainManager getDelegate() { + return getToolchainManager(lookup, logger); + } + + private org.apache.maven.impl.DefaultToolchainManager getToolchainManager(Lookup lookup, Logger logger) { + return getToolchainManager( + lookup.lookupMap(ToolchainFactory.class), + lookup.lookupMap(org.apache.maven.api.services.ToolchainFactory.class), + logger); + } + + private org.apache.maven.impl.DefaultToolchainManager getToolchainManager( + Map v3Factories, + Map v4Factories, + Logger logger) { + Map allFactories = new HashMap<>(); + for (Map.Entry entry : v3Factories.entrySet()) { + ToolchainFactory v3Factory = entry.getValue(); + allFactories.put(entry.getKey(), new org.apache.maven.api.services.ToolchainFactory() { + @Nonnull + @Override + public org.apache.maven.api.Toolchain createToolchain( + @Nonnull org.apache.maven.api.toolchain.ToolchainModel model) throws ToolchainFactoryException { + try { + return getToolchainV4(v3Factory.createToolchain(new ToolchainModel(model))); + } catch (MisconfiguredToolchainException e) { + throw new RuntimeException(e); + } + } + + @Nonnull + @Override + public Optional createDefaultToolchain() + throws ToolchainFactoryException { + return Optional.ofNullable(v3Factory.createDefaultToolchain()) + .map(ToolchainManagerFactory.this::getToolchainV4); + } + }); + } + allFactories.putAll(v4Factories); + return new org.apache.maven.impl.DefaultToolchainManager(allFactories, logger) {}; + } + + public class DefaultToolchainManagerV4 implements org.apache.maven.api.services.ToolchainManager { + @Nonnull + @Override + public List getToolchains( + @Nonnull Session session, @Nonnull String type, @Nullable Map requirements) + throws ToolchainManagerException { + return getDelegate().getToolchains(session, type, requirements); + } + + @Nonnull + @Override + public Optional getToolchainFromBuildContext( + @Nonnull Session session, @Nonnull String type) throws ToolchainManagerException { + return getDelegate().getToolchainFromBuildContext(session, type); + } + + @Override + public void storeToolchainToBuildContext( + @Nonnull Session session, @Nonnull org.apache.maven.api.Toolchain toolchain) { + getDelegate().storeToolchainToBuildContext(session, toolchain); + } + + @Nonnull + @Override + public List getToolchains(@Nonnull Session session, @Nonnull String type) + throws ToolchainManagerException { + return getDelegate().getToolchains(session, type); + } + } + + public class DefaultToolchainManagerV3 implements ToolchainManager, ToolchainManagerPrivate { + + @Override + public Toolchain getToolchainFromBuildContext(String type, MavenSession session) { + return getDelegate() + .getToolchainFromBuildContext(session.getSession(), type) + .map(ToolchainManagerFactory.this::getToolchainV3) + .orElse(null); + } + + @Override + public List getToolchains(MavenSession session, String type, Map requirements) { + return new MappedList<>( + getDelegate().getToolchains(session.getSession(), type, requirements), + ToolchainManagerFactory.this::getToolchainV3); + } + + @Override + public ToolchainPrivate[] getToolchainsForType(String type, MavenSession session) + throws MisconfiguredToolchainException { + try { + List toolchains = + getDelegate().getToolchains(session.getSession(), type); + return toolchains.stream() + .map(ToolchainManagerFactory.this::getToolchainV3) + .toArray(ToolchainPrivate[]::new); + } catch (org.apache.maven.api.services.ToolchainManagerException e) { + throw new MisconfiguredToolchainException(e.getMessage(), e); + } + } + + @Override + public void storeToolchainToBuildContext(ToolchainPrivate toolchain, MavenSession session) { + org.apache.maven.api.Toolchain tc = getToolchainV4(toolchain); + getDelegate().storeToolchainToBuildContext(session.getSession(), tc); + } + } + + private org.apache.maven.api.Toolchain getToolchainV4(ToolchainPrivate toolchain) { + return toolchain instanceof ToolchainWrapperV3 v3tc ? v3tc.delegate : new ToolchainWrapperV4(toolchain); + } + + private ToolchainPrivate getToolchainV3(org.apache.maven.api.Toolchain toolchain) { + return toolchain instanceof ToolchainWrapperV4 v3tc ? v3tc.delegate : new ToolchainWrapperV3(toolchain); + } + + private record ToolchainWrapperV4(ToolchainPrivate delegate) implements org.apache.maven.api.Toolchain { + + @Override + public String getType() { + return delegate.getType(); + } + + @Override + public String findTool(String toolName) { + return delegate.findTool(toolName); + } + + @Override + public org.apache.maven.api.toolchain.ToolchainModel getModel() { + return delegate.getModel().getDelegate(); + } + + @Override + public boolean matchesRequirements(Map requirements) { + return delegate.matchesRequirements(requirements); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + private record ToolchainWrapperV3(org.apache.maven.api.Toolchain delegate) implements Toolchain, ToolchainPrivate { + + @Override + public String getType() { + return delegate.getType(); + } + + @Override + public String findTool(String toolName) { + return delegate.findTool(toolName); + } + + @Override + public boolean matchesRequirements(Map requirements) { + return delegate.matchesRequirements(requirements); + } + + @Override + public ToolchainModel getModel() { + return new ToolchainModel(delegate.getModel()); + } + + @Override + public String toString() { + return delegate.toString(); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/toolchain/ToolchainManagerPrivate.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManagerPrivate.java similarity index 81% rename from maven-core/src/main/java/org/apache/maven/toolchain/ToolchainManagerPrivate.java rename to compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManagerPrivate.java index 6836bf1c7985..f0c93a690619 100644 --- a/maven-core/src/main/java/org/apache/maven/toolchain/ToolchainManagerPrivate.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainManagerPrivate.java @@ -1,5 +1,3 @@ -package org.apache.maven.toolchain; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.toolchain; import org.apache.maven.execution.MavenSession; @@ -28,22 +27,21 @@ *
  • to store chosen toolchain into build context for later use by toolchain-aware plugins.
  • * * - * @author mkleint * @since 2.0.9 * @see ToolchainManager#getToolchainFromBuildContext(String, MavenSession) + * @deprecated Use {@link org.apache.maven.api.services.ToolchainManager} instead. */ -public interface ToolchainManagerPrivate -{ +@Deprecated(since = "4.0.0") +public interface ToolchainManagerPrivate { /** * Retrieves every toolchains of given type available in user settings. - * + * * @param type the type, must not be {@code null} * @param context the Maven session, must not be {@code null} * @since 3.0 (addition of the MavenSession parameter) */ - ToolchainPrivate[] getToolchainsForType( String type, MavenSession context ) - throws MisconfiguredToolchainException; + ToolchainPrivate[] getToolchainsForType(String type, MavenSession context) throws MisconfiguredToolchainException; /** * Stores the toolchain into build context for later use by toolchain-aware plugins. @@ -53,6 +51,5 @@ ToolchainPrivate[] getToolchainsForType( String type, MavenSession context ) * @since 2.0.9 * @see ToolchainManager#getToolchainFromBuildContext(String, MavenSession) */ - void storeToolchainToBuildContext( ToolchainPrivate toolchain, MavenSession context ); - + void storeToolchainToBuildContext(ToolchainPrivate toolchain, MavenSession context); } diff --git a/maven-core/src/main/java/org/apache/maven/toolchain/ToolchainPrivate.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainPrivate.java similarity index 83% rename from maven-core/src/main/java/org/apache/maven/toolchain/ToolchainPrivate.java rename to compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainPrivate.java index 3b739100723a..66a6657d30f4 100644 --- a/maven-core/src/main/java/org/apache/maven/toolchain/ToolchainPrivate.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainPrivate.java @@ -1,5 +1,3 @@ -package org.apache.maven.toolchain; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.toolchain; import java.util.Map; @@ -25,11 +24,11 @@ /** * a private contract between the toolchains plugin and the components. - * @author mkleint + * + * @deprecated Use {@link org.apache.maven.api.Toolchain} instead. */ -public interface ToolchainPrivate - extends Toolchain -{ +@Deprecated(since = "4.0.0") +public interface ToolchainPrivate extends Toolchain { /** * Let the toolchain decide if it matches requirements defined @@ -37,12 +36,11 @@ public interface ToolchainPrivate * @param requirements Map<String, String> key value pair, may not be {@code null} * @return {@code true} if the requirements match, otherwise {@code false} */ - boolean matchesRequirements( Map requirements ); + boolean matchesRequirements(Map requirements); /** - * + * * @return the original model wrapped by this interface */ ToolchainModel getModel(); - -} \ No newline at end of file +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainsBuilder.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainsBuilder.java new file mode 100644 index 000000000000..2f16b8f6ce41 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/ToolchainsBuilder.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import java.io.File; + +import org.apache.maven.toolchain.model.PersistedToolchains; + +/** + * Builds the toolchains model from a previously configured filesystem path to the toolchains file. + * Note: This is an internal component whose interface can change without prior notice. + * + * @deprecated use {@link org.apache.maven.toolchain.building.ToolchainsBuilder} instead + */ +@Deprecated +public interface ToolchainsBuilder { + + /** + * Builds the toolchains model from the configured toolchain files. + * + * @param userToolchainsFile The path to the toolchains file, may be null to disable parsing. + * @return The toolchains model or null if no toolchain file was configured or the configured file does + * not exist. + * @throws MisconfiguredToolchainException If the toolchain file exists but cannot be parsed. + */ + PersistedToolchains build(File userToolchainsFile) throws MisconfiguredToolchainException; +} diff --git a/maven-core/src/main/java/org/apache/maven/toolchain/java/DefaultJavaToolChain.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/DefaultJavaToolChain.java similarity index 78% rename from maven-core/src/main/java/org/apache/maven/toolchain/java/DefaultJavaToolChain.java rename to compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/DefaultJavaToolChain.java index 9cfcff385554..0434bc0a1a02 100644 --- a/maven-core/src/main/java/org/apache/maven/toolchain/java/DefaultJavaToolChain.java +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/DefaultJavaToolChain.java @@ -1,5 +1,3 @@ -package org.apache.maven.toolchain.java; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,38 +16,34 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.toolchain.java; import org.apache.maven.toolchain.model.ToolchainModel; -import org.codehaus.plexus.logging.Logger; +import org.slf4j.Logger; /** * Provides backwards compatibility with Maven 3.2.3 and earlier. Clients that do not require compatibility with Maven * 3.2.3 and earlier are encouraged to use {@link JavaToolchainImpl}. * Note: This is an internal component whose interface can change without prior notice. - * + * * @deprecated clients that do not require compatibility with Maven 3.2.3 and earlier should link to * {@link JavaToolchainImpl} instead. */ -public class DefaultJavaToolChain - extends JavaToolchainImpl -{ +@Deprecated +public class DefaultJavaToolChain extends JavaToolchainImpl { public static final String KEY_JAVAHOME = JavaToolchainImpl.KEY_JAVAHOME; - public DefaultJavaToolChain( ToolchainModel model, Logger logger ) - { - super( model, logger ); + public DefaultJavaToolChain(ToolchainModel model, Logger logger) { + super(model, logger); } @Override - public String getJavaHome() - { + public String getJavaHome() { return super.getJavaHome(); } @Override - public void setJavaHome( String javaHome ) - { - super.setJavaHome( javaHome ); + public void setJavaHome(String javaHome) { + super.setJavaHome(javaHome); } - -} \ No newline at end of file +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchain.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchain.java new file mode 100644 index 000000000000..6080f00f4d79 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchain.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain.java; + +import org.apache.maven.toolchain.Toolchain; + +/** + * JDK toolchain interface. + * + * @since 2.0.9, renamed from JavaToolChain in 3.2.4 + * @deprecated Use {@link org.apache.maven.api.JavaToolchain} instead. + */ +@Deprecated(since = "4.0.0") +public interface JavaToolchain extends Toolchain {} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchainFactory.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchainFactory.java new file mode 100644 index 000000000000..f22884ce6494 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchainFactory.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain.java; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.maven.toolchain.MisconfiguredToolchainException; +import org.apache.maven.toolchain.RequirementMatcher; +import org.apache.maven.toolchain.RequirementMatcherFactory; +import org.apache.maven.toolchain.ToolchainFactory; +import org.apache.maven.toolchain.ToolchainPrivate; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JDK toolchain factory. + * This is a ToolchainFactory Plexus component registered with + * jdk hint. + * + * @since 2.0.9, renamed from DefaultJavaToolchainFactory in 3.2.4 + * @deprecated Use {@link org.apache.maven.api.services.ToolchainFactory} instead. + */ +@Deprecated(since = "4.0.0") +public class JavaToolchainFactory implements ToolchainFactory { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public ToolchainPrivate createToolchain(ToolchainModel model) throws MisconfiguredToolchainException { + if (model == null) { + return null; + } + + // use DefaultJavaToolChain for compatibility with maven 3.2.3 and earlier + + @SuppressWarnings("deprecation") + JavaToolchainImpl jtc = new DefaultJavaToolChain(model, logger); + + // populate the provides section + Properties provides = model.getProvides(); + for (Entry provide : provides.entrySet()) { + String key = (String) provide.getKey(); + String value = (String) provide.getValue(); + + if (value == null) { + throw new MisconfiguredToolchainException( + "Provides token '" + key + "' doesn't have any value configured."); + } + + RequirementMatcher matcher; + if ("version".equals(key)) { + matcher = RequirementMatcherFactory.createVersionMatcher(value); + } else { + matcher = RequirementMatcherFactory.createExactMatcher(value); + } + + jtc.addProvideToken(key, matcher); + } + + // populate the configuration section + Xpp3Dom dom = (Xpp3Dom) model.getConfiguration(); + Xpp3Dom javahome = dom != null ? dom.getChild(JavaToolchainImpl.KEY_JAVAHOME) : null; + if (javahome == null) { + throw new MisconfiguredToolchainException( + "Java toolchain without the " + JavaToolchainImpl.KEY_JAVAHOME + " configuration element."); + } + Path normal = Paths.get(javahome.getValue()).normalize(); + if (Files.exists(normal)) { + jtc.setJavaHome(Paths.get(javahome.getValue()).normalize().toString()); + } else { + throw new MisconfiguredToolchainException( + "Non-existing JDK home configuration at " + normal.toAbsolutePath()); + } + + return jtc; + } + + @Override + public ToolchainPrivate createDefaultToolchain() { + // not sure it's necessary to provide a default toolchain here. + // only version can be eventually supplied. + return null; + } + + protected Logger getLogger() { + return logger; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchainImpl.java b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchainImpl.java new file mode 100644 index 000000000000..661ffa642455 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/toolchain/java/JavaToolchainImpl.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain.java; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.maven.toolchain.DefaultToolchain; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.apache.maven.utils.Os; +import org.slf4j.Logger; + +/** + * JDK toolchain implementation. + * + * @since 2.0.9, renamed from DefaultJavaToolChain in 3.2.4 + * @deprecated Use {@link org.apache.maven.api.services.ToolchainFactory} instead. + */ +@Deprecated(since = "4.0.0") +public class JavaToolchainImpl extends DefaultToolchain implements JavaToolchain { + private String javaHome; + + public static final String KEY_JAVAHOME = "jdkHome"; // NOI18N + + JavaToolchainImpl(ToolchainModel model, Logger logger) { + super(model, "jdk", logger); + } + + public String getJavaHome() { + return javaHome; + } + + public void setJavaHome(String javaHome) { + this.javaHome = javaHome; + } + + @Override + public String toString() { + return "JDK[" + getJavaHome() + "]"; + } + + @Override + public String findTool(String toolName) { + Path toRet = findTool(toolName, Paths.get(getJavaHome()).normalize()); + if (toRet != null) { + return toRet.toAbsolutePath().toString(); + } + return null; + } + + private static Path findTool(String toolName, Path installDir) { + Path bin = installDir.resolve("bin"); // NOI18N + if (Files.isDirectory(bin)) { + if (Os.IS_WINDOWS) { + Path tool = bin.resolve(toolName + ".exe"); + if (Files.exists(tool)) { + return tool; + } + } + Path tool = bin.resolve(toolName); + if (Files.exists(tool)) { + return tool; + } + } + return null; + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/usability/plugin/ExpressionDocumentationException.java b/compat/maven-compat/src/main/java/org/apache/maven/usability/plugin/ExpressionDocumentationException.java new file mode 100644 index 000000000000..1378ab248b42 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/usability/plugin/ExpressionDocumentationException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.usability.plugin; + +/** + * ExpressionDocumentationException + */ +@Deprecated +public class ExpressionDocumentationException extends Exception { + static final long serialVersionUID = 1; + + public ExpressionDocumentationException(String message, Throwable cause) { + super(message, cause); + } + + public ExpressionDocumentationException(String message) { + super(message); + } +} diff --git a/compat/maven-compat/src/main/java/org/apache/maven/usability/plugin/ExpressionDocumenter.java b/compat/maven-compat/src/main/java/org/apache/maven/usability/plugin/ExpressionDocumenter.java new file mode 100644 index 000000000000..55b1a4ac1700 --- /dev/null +++ b/compat/maven-compat/src/main/java/org/apache/maven/usability/plugin/ExpressionDocumenter.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.usability.plugin; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.usability.plugin.io.xpp3.ParamdocXpp3Reader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * ExpressionDocumenter + */ +@Deprecated +public class ExpressionDocumenter { + + private static final String[] EXPRESSION_ROOTS = {"project", "settings", "session", "plugin", "rootless"}; + + private static final String EXPRESSION_DOCO_ROOTPATH = "META-INF/maven/plugin-expressions/"; + + private static Map expressionDocumentation; + + public static Map load() throws ExpressionDocumentationException { + if (expressionDocumentation == null) { + expressionDocumentation = new HashMap<>(); + + ClassLoader docLoader = initializeDocLoader(); + + for (String root : EXPRESSION_ROOTS) { + try (InputStream docStream = + docLoader.getResourceAsStream(EXPRESSION_DOCO_ROOTPATH + root + ".paramdoc.xml")) { + if (docStream != null) { + Map doco = parseExpressionDocumentation(docStream); + + expressionDocumentation.putAll(doco); + } + } catch (IOException e) { + throw new ExpressionDocumentationException( + "Failed to read documentation for expression root: " + root, e); + } catch (XmlPullParserException e) { + throw new ExpressionDocumentationException( + "Failed to parse documentation for expression root: " + root, e); + } + } + } + + return expressionDocumentation; + } + + /** + * + * + * project.distributionManagementArtifactRepository + * + * + * some-repo + * scp://host/path + * + * + * some-snap-repo + * scp://host/snapshot-path + * + * + * ]]> + * + * + * + * + * @throws IOException + * @throws XmlPullParserException + */ + private static Map parseExpressionDocumentation(InputStream docStream) + throws IOException, XmlPullParserException { + ParamdocXpp3Reader paramdocReader = new ParamdocXpp3Reader(); + + ExpressionDocumentation documentation = paramdocReader.read(docStream, true); + + List expressions = documentation.getExpressions(); + + Map bySyntax = new HashMap<>(); + + if (expressions != null && !expressions.isEmpty()) { + for (Expression expression : expressions) { + bySyntax.put(expression.getSyntax(), expression); + } + } + + return bySyntax; + } + + private static ClassLoader initializeDocLoader() throws ExpressionDocumentationException { + String myResourcePath = ExpressionDocumenter.class.getName().replace('.', '/') + ".class"; + + URL myResource = ExpressionDocumenter.class.getClassLoader().getResource(myResourcePath); + + assert myResource != null : "The resource is this class itself loaded by its own classloader and must exist"; + + String myClasspathEntry = myResource.getPath(); + + myClasspathEntry = myClasspathEntry.substring(0, myClasspathEntry.length() - (myResourcePath.length() + 2)); + + if (myClasspathEntry.startsWith("file:")) { + myClasspathEntry = myClasspathEntry.substring("file:".length()); + } + + URL docResource; + try { + docResource = new File(myClasspathEntry).toURL(); + } catch (MalformedURLException e) { + throw new ExpressionDocumentationException( + "Cannot construct expression documentation classpath" + " resource base.", e); + } + + return new URLClassLoader(new URL[] {docResource}); + } +} diff --git a/maven-compat/src/main/mdo/paramdoc.mdo b/compat/maven-compat/src/main/mdo/paramdoc.mdo similarity index 100% rename from maven-compat/src/main/mdo/paramdoc.mdo rename to compat/maven-compat/src/main/mdo/paramdoc.mdo diff --git a/compat/maven-compat/src/main/mdo/profiles.mdo b/compat/maven-compat/src/main/mdo/profiles.mdo new file mode 100644 index 000000000000..8aa8a2997919 --- /dev/null +++ b/compat/maven-compat/src/main/mdo/profiles.mdo @@ -0,0 +1,400 @@ + + + + profiles + Profiles + Deprecated Project-local overrides to the build process based on detected or user-provided environmental parameters. + This is the model specification for ${basedir}/profiles.xml. + ]]> + + + package + org.apache.maven.profiles + + + + + ProfilesRoot + 1.0.0 + Root element of the profiles.xml file. + + + profiles + 1.0.0 + + + Profile + * + + + + activeProfiles + 1.0.0 + + + String + * + + + + + + Profile + 1.0.0 + + + + id + true + 1.0.0 + String + The ID of this build profile, for activation + purposes. + + + activation + 1.0.0 + + + Activation + + + + properties + Extended configuration specific to this profile goes + here. + Properties + + String + * + + + + repositories + 1.0.0 + + + + Repository + * + + + + pluginRepositories + 1.0.0 + + + Repository + * + + + + + + + Activation + 1.0.0 + + + + activeByDefault + 1.0.0 + boolean + Flag specifying whether this profile is active as a default. + + + jdk + 1.0.0 + String + + + + os + 1.0.0 + + + ActivationOS + + + + property + 1.0.0 + + + ActivationProperty + + + + file + 1.0.0 + + + ActivationFile + + + + + + + + RepositoryBase + 1.0.0 + + + + id + 1.0.0 + + String + + + name + 1.0.0 + + String + + + url + 1.0.0 + + String + + + layout + 1.0.0 + The type of layout this repository uses for locating and storing artifacts - can be "legacy" or + "default". + String + default + + + + + 1.0.0 + + + + + + Repository + RepositoryBase + 1.0.0 + + Repository contains the information needed for establishing connections with remote repository + + + + releases + 1.0.0 + How to handle downloading of releases from this repository + + RepositoryPolicy + + + + snapshots + 1.0.0 + How to handle downloading of snapshots from this repository + + RepositoryPolicy + + + + + + + 1.0.0 + + + + + + + RepositoryPolicy + 1.0.0 + Download policy + + + enabled + 1.0.0 + Whether to use this repository for downloading this type of artifact + boolean + true + + + updatePolicy + 1.0.0 + + The frequency for downloading updates - can be "always", "daily" (default), "interval:XXX" (in minutes) or + "never" (only if it doesn't exist locally). + + String + + + checksumPolicy + 1.0.0 + + What to do when verification of an artifact checksum fails. Valid values are "fail" (default for Maven 4 and + above), "warn" (default for Maven 3) or "ignore". + + String + + + + + ActivationProperty + 1.0.0 + + + + name + 1.0.0 + String + true + The name of the property to be used to activate a profile + + + value + 1.0.0 + String + The value of the property to be used to activate a profile + + + + + ActivationFile + 1.0.0 + + + + missing + 1.0.0 + String + The name of the file that should be missing to activate a profile + + + exists + 1.0.0 + String + The name of the file that should exist to activate a profile + + + + + ActivationOS + 1.0.0 + + + + name + 1.0.0 + String + The name of the OS to be used to activate a profile + + + family + 1.0.0 + String + The general family of the OS to be used to activate a profile (e.g. 'windows') + + + arch + 1.0.0 + String + The architecture of the OS to be used to activate a profile + + + version + 1.0.0 + String + The version of the OS to be used to activate a profile + + + + + diff --git a/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/project.paramdoc.xml b/compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/project.paramdoc.xml similarity index 100% rename from maven-compat/src/main/resources/META-INF/maven/plugin-expressions/project.paramdoc.xml rename to compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/project.paramdoc.xml diff --git a/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/rootless.paramdoc.xml b/compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/rootless.paramdoc.xml similarity index 100% rename from maven-compat/src/main/resources/META-INF/maven/plugin-expressions/rootless.paramdoc.xml rename to compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/rootless.paramdoc.xml diff --git a/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/settings.paramdoc.xml b/compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/settings.paramdoc.xml similarity index 93% rename from maven-compat/src/main/resources/META-INF/maven/plugin-expressions/settings.paramdoc.xml rename to compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/settings.paramdoc.xml index e058218a461a..36c61e8ba02f 100644 --- a/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/settings.paramdoc.xml +++ b/compat/maven-compat/src/main/resources/META-INF/maven/plugin-expressions/settings.paramdoc.xml @@ -46,8 +46,8 @@ under the License. If false, flags the system to skip prompting the user for any information, or holding up the build waiting for any input. - NOTE: It's also possible to switch to batch (ie. non-interactive) mode using the '-B' command-line option. + NOTE: It's also possible to switch to batch (i.e. non-interactive) mode using the '-B' command-line option. ]]> - \ No newline at end of file + diff --git a/maven-compat/src/site/apt/index.apt b/compat/maven-compat/src/site/apt/index.apt similarity index 100% rename from maven-compat/src/site/apt/index.apt rename to compat/maven-compat/src/site/apt/index.apt diff --git a/compat/maven-compat/src/site/site.xml b/compat/maven-compat/src/site/site.xml new file mode 100644 index 000000000000..4ee3b709cfc4 --- /dev/null +++ b/compat/maven-compat/src/site/site.xml @@ -0,0 +1,35 @@ + + + + + + + ${project.scm.url} + + + + + + + + + + diff --git a/compat/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java b/compat/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java new file mode 100644 index 000000000000..436411615668 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java @@ -0,0 +1,317 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven; + +import javax.inject.Inject; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.apache.maven.api.Session; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.impl.InternalSession; +import org.apache.maven.internal.impl.DefaultLookup; +import org.apache.maven.internal.impl.DefaultSessionFactory; +import org.apache.maven.internal.impl.InternalMavenSession; +import org.apache.maven.model.Build; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Exclusion; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Repository; +import org.apache.maven.model.RepositoryPolicy; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.repository.internal.MavenSessionBuilderSupplier; +import org.apache.maven.session.scope.internal.SessionScope; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.testing.PlexusTest; +import org.codehaus.plexus.util.FileUtils; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.LocalRepository; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; + +@PlexusTest +@Deprecated +public abstract class AbstractCoreMavenComponentTestCase { + + @Inject + protected PlexusContainer container; + + @Inject + protected org.eclipse.aether.RepositorySystem repositorySystem; + + @Inject + protected RepositorySystem mavenRepositorySystem; + + @Inject + protected org.apache.maven.project.ProjectBuilder projectBuilder; + + protected abstract String getProjectsDirectory(); + + protected PlexusContainer getContainer() { + return container; + } + + protected File getProject(String name) throws Exception { + File source = new File(new File(getBasedir(), getProjectsDirectory()), name); + File target = new File(new File(getBasedir(), "target"), name); + FileUtils.copyDirectoryStructureIfModified(source, target); + return new File(target, "pom.xml"); + } + + protected MavenExecutionRequest createMavenExecutionRequest(File pom) throws Exception { + MavenExecutionRequest request = new DefaultMavenExecutionRequest() + .setPom(pom) + .setProjectPresent(true) + .setShowErrors(true) + .setPluginGroups(Arrays.asList("org.apache.maven.plugins")) + .setLocalRepository(getLocalRepository()) + .setRemoteRepositories(getRemoteRepositories()) + .setPluginArtifactRepositories(getPluginArtifactRepositories()) + .setGoals(Arrays.asList("package")); + + if (pom != null) { + request.setMultiModuleProjectDirectory(pom.getParentFile()); + } + + return request; + } + + // layer the creation of a project builder configuration with a request, but this will need to be + // a Maven subclass because we don't want to couple maven to the project builder which we need to + // separate. + protected MavenSession createMavenSession(File pom) throws Exception { + return createMavenSession(pom, new Properties()); + } + + protected MavenSession createMavenSession(File pom, Properties executionProperties) throws Exception { + return createMavenSession(pom, executionProperties, false); + } + + protected MavenSession createMavenSession(File pom, Properties executionProperties, boolean includeModules) + throws Exception { + MavenExecutionRequest request = createMavenExecutionRequest(pom); + + ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest() + .setLocalRepository(request.getLocalRepository()) + .setRemoteRepositories(request.getRemoteRepositories()) + .setPluginArtifactRepositories(request.getPluginArtifactRepositories()) + .setSystemProperties(executionProperties) + .setUserProperties(new Properties()); + + initRepoSession(request, configuration); + + List projects = new ArrayList<>(); + + if (pom != null) { + MavenProject project = projectBuilder.build(pom, configuration).getProject(); + + projects.add(project); + if (includeModules) { + for (String module : project.getModules()) { + File modulePom = new File(pom.getParentFile(), module); + if (modulePom.isDirectory()) { + modulePom = new File(modulePom, "pom.xml"); + } + projects.add(projectBuilder.build(modulePom, configuration).getProject()); + } + } + } else { + MavenProject project = createStubMavenProject(); + project.setRemoteArtifactRepositories(request.getRemoteRepositories()); + project.setPluginArtifactRepositories(request.getPluginArtifactRepositories()); + projects.add(project); + } + + InternalSession iSession = InternalSession.from(configuration.getRepositorySession()); + InternalMavenSession mSession = InternalMavenSession.from(iSession); + MavenSession session = mSession.getMavenSession(); + + session.setProjects(projects); + session.setAllProjects(session.getProjects()); + + return session; + } + + protected void initRepoSession( + MavenExecutionRequest mavenExecutionRequest, ProjectBuildingRequest projectBuildingRequest) + throws Exception { + File localRepoDir = new File(projectBuildingRequest.getLocalRepository().getBasedir()); + LocalRepository localRepo = new LocalRepository(localRepoDir, "simple"); + + RepositorySystemSession session = new MavenSessionBuilderSupplier(repositorySystem) + .get() + .withLocalRepositories(localRepo) + .build(); + projectBuildingRequest.setRepositorySession(session); + + DefaultSessionFactory defaultSessionFactory = + new DefaultSessionFactory(repositorySystem, null, new DefaultLookup(container), null); + + MavenSession mSession = new MavenSession( + container, + projectBuildingRequest.getRepositorySession(), + mavenExecutionRequest, + new DefaultMavenExecutionResult()); + + InternalSession iSession = defaultSessionFactory.newSession(mSession); + mSession.setSession(iSession); + + SessionScope sessionScope = getContainer().lookup(SessionScope.class); + sessionScope.enter(); + sessionScope.seed(MavenSession.class, mSession); + sessionScope.seed(Session.class, iSession); + sessionScope.seed(InternalMavenSession.class, InternalMavenSession.from(iSession)); + } + + protected MavenProject createStubMavenProject() { + Model model = new Model(); + model.setGroupId("org.apache.maven.test"); + model.setArtifactId("maven-test"); + model.setVersion("1.0"); + return new MavenProject(model); + } + + protected List getRemoteRepositories() throws InvalidRepositoryException { + File repoDir = new File(getBasedir(), "src/test/remote-repo").getAbsoluteFile(); + + RepositoryPolicy policy = new RepositoryPolicy(); + policy.setEnabled(true); + policy.setChecksumPolicy("ignore"); + policy.setUpdatePolicy("always"); + + Repository repository = new Repository(); + repository.setId(MavenRepositorySystem.DEFAULT_REMOTE_REPO_ID); + repository.setUrl("file://" + repoDir.toURI().getPath()); + repository.setReleases(policy); + repository.setSnapshots(policy); + + return Arrays.asList(mavenRepositorySystem.buildArtifactRepository(repository)); + } + + protected List getPluginArtifactRepositories() throws InvalidRepositoryException { + return getRemoteRepositories(); + } + + protected ArtifactRepository getLocalRepository() throws InvalidRepositoryException { + File repoDir = new File(getBasedir(), "target/local-repo").getAbsoluteFile(); + + return mavenRepositorySystem.createLocalRepository(repoDir); + } + + protected class ProjectBuilder { + private MavenProject project; + + public ProjectBuilder(MavenProject project) { + this.project = project; + } + + public ProjectBuilder(String groupId, String artifactId, String version) { + Model model = new Model(); + model.setModelVersion("4.0.0"); + model.setGroupId(groupId); + model.setArtifactId(artifactId); + model.setVersion(version); + model.setBuild(new Build()); + project = new MavenProject(model); + } + + public ProjectBuilder setGroupId(String groupId) { + project.setGroupId(groupId); + return this; + } + + public ProjectBuilder setArtifactId(String artifactId) { + project.setArtifactId(artifactId); + return this; + } + + public ProjectBuilder setVersion(String version) { + project.setVersion(version); + return this; + } + + // Dependencies + // + public ProjectBuilder addDependency(String groupId, String artifactId, String version, String scope) { + return addDependency(groupId, artifactId, version, scope, (Exclusion) null); + } + + public ProjectBuilder addDependency( + String groupId, String artifactId, String version, String scope, Exclusion exclusion) { + return addDependency(groupId, artifactId, version, scope, null, exclusion); + } + + public ProjectBuilder addDependency( + String groupId, String artifactId, String version, String scope, String systemPath) { + return addDependency(groupId, artifactId, version, scope, systemPath, null); + } + + public ProjectBuilder addDependency( + String groupId, + String artifactId, + String version, + String scope, + String systemPath, + Exclusion exclusion) { + Dependency d = new Dependency(); + d.setGroupId(groupId); + d.setArtifactId(artifactId); + d.setVersion(version); + d.setScope(scope); + + if (systemPath != null && scope.equals(Artifact.SCOPE_SYSTEM)) { + d.setSystemPath(systemPath); + } + + if (exclusion != null) { + d.addExclusion(exclusion); + } + + project.getDependencies().add(d); + + return this; + } + + // Plugins + // + public ProjectBuilder addPlugin(Plugin plugin) { + project.getBuildPlugins().add(plugin); + return this; + } + + public MavenProject get() { + return project; + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java new file mode 100644 index 000000000000..723ef0111d00 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven; + +import javax.inject.Inject; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Deprecated +class ProjectDependenciesResolverTest extends AbstractCoreMavenComponentTestCase { + @Inject + private ProjectDependenciesResolver resolver; + + @Override + protected String getProjectsDirectory() { + return "src/test/projects/project-dependencies-resolver"; + } + + @Test + void testSystemScopeDependencies() throws Exception { + MavenSession session = createMavenSession(null); + MavenProject project = session.getCurrentProject(); + + new ProjectBuilder(project) + .addDependency( + "com.mycompany", + "system-dependency", + "1.0", + Artifact.SCOPE_SYSTEM, + new File(getBasedir(), "pom.xml").getAbsolutePath()); + + Set artifactDependencies = + resolver.resolve(project, Collections.singleton(Artifact.SCOPE_COMPILE), session); + assertEquals(1, artifactDependencies.size()); + } + + @Test + void testSystemScopeDependencyIsPresentInTheCompileClasspathElements() throws Exception { + File pom = getProject("it0063"); + + Properties eps = new Properties(); + eps.setProperty("jre.home", new File(pom.getParentFile(), "jdk/jre").getPath()); + + MavenSession session = createMavenSession(pom, eps); + MavenProject project = session.getCurrentProject(); + + project.setArtifacts(resolver.resolve(project, Collections.singleton(Artifact.SCOPE_COMPILE), session)); + + List elements = project.getCompileClasspathElements(); + assertEquals(2, elements.size()); + + @SuppressWarnings("deprecation") + List artifacts = project.getCompileArtifacts(); + assertEquals(1, artifacts.size()); + assertThat(artifacts.get(0).getFile().getName(), endsWith("tools.jar")); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/SimpleLookup.java b/compat/maven-compat/src/test/java/org/apache/maven/SimpleLookup.java new file mode 100644 index 000000000000..79370b9982ac --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/SimpleLookup.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.services.Lookup; +import org.apache.maven.api.services.LookupException; + +public class SimpleLookup implements Lookup { + private final List objects; + + public SimpleLookup(List objects) { + this.objects = objects; + } + + @Override + public T lookup(Class type) { + return objects.stream() + .filter(type::isInstance) + .map(type::cast) + .findAny() + .orElseThrow(() -> new LookupException("No service of type " + type)); + } + + @Override + public T lookup(Class type, String name) { + return null; + } + + @Override + public Optional lookupOptional(Class type) { + return Optional.empty(); + } + + @Override + public Optional lookupOptional(Class type, String name) { + return Optional.empty(); + } + + @Override + public List lookupList(Class type) { + return List.of(); + } + + @Override + public Map lookupMap(Class type) { + return Map.of(); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/AbstractArtifactComponentTestCase.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/AbstractArtifactComponentTestCase.java new file mode 100644 index 000000000000..14f394c0c1cd --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/AbstractArtifactComponentTestCase.java @@ -0,0 +1,347 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.api.DependencyScope; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.internal.impl.DefaultLookup; +import org.apache.maven.internal.impl.DefaultSessionFactory; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.internal.artifact.FatArtifactTraverser; +import org.apache.maven.repository.internal.scopes.Maven4ScopeManagerConfiguration; +import org.apache.maven.repository.legacy.repository.ArtifactRepositoryFactory; +import org.apache.maven.rtinfo.RuntimeInformation; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.testing.PlexusTest; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.collection.DependencyGraphTransformer; +import org.eclipse.aether.collection.DependencyManager; +import org.eclipse.aether.collection.DependencySelector; +import org.eclipse.aether.collection.DependencyTraverser; +import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; +import org.eclipse.aether.internal.impl.scope.ManagedDependencyContextRefiner; +import org.eclipse.aether.internal.impl.scope.ManagedScopeDeriver; +import org.eclipse.aether.internal.impl.scope.ManagedScopeSelector; +import org.eclipse.aether.internal.impl.scope.OptionalDependencySelector; +import org.eclipse.aether.internal.impl.scope.ScopeDependencySelector; +import org.eclipse.aether.internal.impl.scope.ScopeManagerImpl; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.util.graph.manager.ClassicDependencyManager; +import org.eclipse.aether.util.graph.selector.AndDependencySelector; +import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector; +import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer; +import org.eclipse.aether.util.graph.transformer.ConflictResolver; +import org.eclipse.aether.util.graph.transformer.NearestVersionSelector; +import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector; +import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy; +import org.junit.jupiter.api.BeforeEach; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + */ +@PlexusTest +@Deprecated +public abstract class AbstractArtifactComponentTestCase // extends PlexusTestCase + { + @Inject + protected ArtifactFactory artifactFactory; + + @Inject + protected ArtifactRepositoryFactory artifactRepositoryFactory; + + @Inject + LegacySupport legacySupport; + + @Inject + @Named("default") + ArtifactRepositoryLayout repoLayout; + + @Inject + PlexusContainer container; + + public PlexusContainer getContainer() { + return container; + } + + @BeforeEach + public void setUp() throws Exception { + RepositorySystemSession repoSession = initRepoSession(); + MavenSession session = new MavenSession( + getContainer(), repoSession, new DefaultMavenExecutionRequest(), new DefaultMavenExecutionResult()); + session.setSession(new DefaultSessionFactory( + getContainer().lookup(RepositorySystem.class), + getContainer().lookup(MavenRepositorySystem.class), + new DefaultLookup(getContainer()), + getContainer().lookup(RuntimeInformation.class)) + .newSession(session)); + + legacySupport.setSession(session); + } + + protected abstract String component(); + + /** + * Return an existing file, not a directory - causes creation to fail. + * + * @throws Exception + */ + protected ArtifactRepository badLocalRepository() throws Exception { + String path = "target/test-repositories/" + component() + "/bad-local-repository"; + + File f = new File(getBasedir(), path); + + f.createNewFile(); + + return artifactRepositoryFactory.createArtifactRepository( + "test", "file://" + f.getPath(), repoLayout, null, null); + } + + protected String getRepositoryLayout() { + return "default"; + } + + protected ArtifactRepository localRepository() throws Exception { + String path = "target/test-repositories/" + component() + "/local-repository"; + + File f = new File(getBasedir(), path); + + return artifactRepositoryFactory.createArtifactRepository( + "local", "file://" + f.getPath(), repoLayout, null, null); + } + + protected ArtifactRepository remoteRepository() throws Exception { + String path = "target/test-repositories/" + component() + "/remote-repository"; + + File f = new File(getBasedir(), path); + + return artifactRepositoryFactory.createArtifactRepository( + "test", + "file://" + f.getPath(), + repoLayout, + new ArtifactRepositoryPolicy(), + new ArtifactRepositoryPolicy()); + } + + protected ArtifactRepository badRemoteRepository() throws Exception { + return artifactRepositoryFactory.createArtifactRepository( + "test", "http://foo.bar/repository", repoLayout, null, null); + } + + protected void assertRemoteArtifactPresent(Artifact artifact) throws Exception { + ArtifactRepository remoteRepo = remoteRepository(); + + String path = remoteRepo.pathOf(artifact); + + File file = new File(remoteRepo.getBasedir(), path); + + assertTrue(file.exists(), "Remote artifact " + file + " should be present."); + } + + protected void assertLocalArtifactPresent(Artifact artifact) throws Exception { + ArtifactRepository localRepo = localRepository(); + + String path = localRepo.pathOf(artifact); + + File file = new File(localRepo.getBasedir(), path); + + assertTrue(file.exists(), "Local artifact " + file + " should be present."); + } + + protected void assertRemoteArtifactNotPresent(Artifact artifact) throws Exception { + ArtifactRepository remoteRepo = remoteRepository(); + + String path = remoteRepo.pathOf(artifact); + + File file = new File(remoteRepo.getBasedir(), path); + + assertFalse(file.exists(), "Remote artifact " + file + " should not be present."); + } + + protected void assertLocalArtifactNotPresent(Artifact artifact) throws Exception { + ArtifactRepository localRepo = localRepository(); + + String path = localRepo.pathOf(artifact); + + File file = new File(localRepo.getBasedir(), path); + + assertFalse(file.exists(), "Local artifact " + file + " should not be present."); + } + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + protected List remoteRepositories() throws Exception { + List remoteRepositories = new ArrayList<>(); + + remoteRepositories.add(remoteRepository()); + + return remoteRepositories; + } + + // ---------------------------------------------------------------------- + // Test artifact generation for unit tests + // ---------------------------------------------------------------------- + + protected Artifact createLocalArtifact(String artifactId, String version) throws Exception { + Artifact artifact = createArtifact(artifactId, version); + + createArtifact(artifact, localRepository()); + + return artifact; + } + + protected Artifact createRemoteArtifact(String artifactId, String version) throws Exception { + Artifact artifact = createArtifact(artifactId, version); + + createArtifact(artifact, remoteRepository()); + + return artifact; + } + + protected void createLocalArtifact(Artifact artifact) throws Exception { + createArtifact(artifact, localRepository()); + } + + protected void createRemoteArtifact(Artifact artifact) throws Exception { + createArtifact(artifact, remoteRepository()); + } + + protected void createArtifact(Artifact artifact, ArtifactRepository repository) throws Exception { + String path = repository.pathOf(artifact); + + File artifactFile = new File(repository.getBasedir(), path); + + if (!artifactFile.getParentFile().exists()) { + artifactFile.getParentFile().mkdirs(); + } + try (Writer writer = new OutputStreamWriter(new FileOutputStream(artifactFile), StandardCharsets.ISO_8859_1)) { + writer.write(artifact.getId()); + } + + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(artifact.getId().getBytes()); + byte[] digest = md.digest(); + + String md5path = repository.pathOf(artifact) + ".md5"; + File md5artifactFile = new File(repository.getBasedir(), md5path); + try (Writer writer = + new OutputStreamWriter(new FileOutputStream(md5artifactFile), StandardCharsets.ISO_8859_1)) { + writer.append(printHexBinary(digest)); + } + } + + protected Artifact createArtifact(String artifactId, String version) throws Exception { + return createArtifact(artifactId, version, "jar"); + } + + protected Artifact createArtifact(String artifactId, String version, String type) throws Exception { + return createArtifact("org.apache.maven", artifactId, version, type); + } + + protected Artifact createArtifact(String groupId, String artifactId, String version, String type) throws Exception { + Artifact a = artifactFactory.createBuildArtifact(groupId, artifactId, version, type); + + return a; + } + + protected void deleteLocalArtifact(Artifact artifact) throws Exception { + deleteArtifact(artifact, localRepository()); + } + + protected void deleteArtifact(Artifact artifact, ArtifactRepository repository) throws Exception { + String path = repository.pathOf(artifact); + + File artifactFile = new File(repository.getBasedir(), path); + + if (artifactFile.exists()) { + if (!artifactFile.delete()) { + throw new IOException("Failure while attempting to delete artifact " + artifactFile); + } + } + } + + protected DefaultRepositorySystemSession initRepoSession() throws Exception { + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(h -> false); + session.setScopeManager(new ScopeManagerImpl(Maven4ScopeManagerConfiguration.INSTANCE)); + session.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(true, true)); + DependencyTraverser depTraverser = new FatArtifactTraverser(); + session.setDependencyTraverser(depTraverser); + + DependencyManager depManager = new ClassicDependencyManager(true, session.getScopeManager()); + session.setDependencyManager(depManager); + + DependencySelector depFilter = new AndDependencySelector( + ScopeDependencySelector.legacy( + null, Arrays.asList(DependencyScope.TEST.id(), DependencyScope.PROVIDED.id())), + OptionalDependencySelector.fromDirect(), + new ExclusionDependencySelector()); + session.setDependencySelector(depFilter); + + ScopeManagerImpl scopeManager = new ScopeManagerImpl(Maven4ScopeManagerConfiguration.INSTANCE); + session.setScopeManager(scopeManager); + DependencyGraphTransformer transformer = new ConflictResolver( + new NearestVersionSelector(), new ManagedScopeSelector(scopeManager), + new SimpleOptionalitySelector(), new ManagedScopeDeriver(scopeManager)); + transformer = + new ChainedDependencyGraphTransformer(transformer, new ManagedDependencyContextRefiner(scopeManager)); + session.setDependencyGraphTransformer(transformer); + + LocalRepository localRepo = new LocalRepository(localRepository().getBasedir()); + session.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory().newInstance(session, localRepo)); + return session; + } + + private static final char[] HEX_CODE = "0123456789ABCDEF".toCharArray(); + + private static String printHexBinary(byte[] data) { + StringBuilder r = new StringBuilder(data.length * 2); + for (byte b : data) { + r.append(HEX_CODE[(b >> 4) & 0xF]); + r.append(HEX_CODE[(b & 0xF)]); + } + return r.toString(); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java new file mode 100644 index 000000000000..0435e1195252 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.deployer; + +import javax.inject.Inject; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import org.apache.maven.artifact.AbstractArtifactComponentTestCase; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.session.scope.internal.SessionScope; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +/** + */ +@Deprecated +class ArtifactDeployerTest extends AbstractArtifactComponentTestCase { + @Inject + private ArtifactDeployer artifactDeployer; + + @Inject + private SessionScope sessionScope; + + protected String component() { + return "deployer"; + } + + @Test + void testArtifactInstallation() throws Exception { + sessionScope.enter(); + try { + sessionScope.seed(MavenSession.class, mock(MavenSession.class)); + + String artifactBasedir = new File(getBasedir(), "src/test/resources/artifact-install").getAbsolutePath(); + + Artifact artifact = createArtifact("artifact", "1.0"); + + File file = new File(artifactBasedir, "artifact-1.0.jar"); + assertEquals("dummy", new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8).trim()); + + artifactDeployer.deploy(file, artifact, remoteRepository(), localRepository()); + + ArtifactRepository remoteRepository = remoteRepository(); + File deployedFile = new File(remoteRepository.getBasedir(), remoteRepository.pathOf(artifact)); + assertTrue(deployedFile.exists()); + assertEquals("dummy", new String(Files.readAllBytes(deployedFile.toPath()), StandardCharsets.UTF_8).trim()); + } finally { + sessionScope.exit(); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/SimpleArtifactMetadataSource.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/SimpleArtifactMetadataSource.java new file mode 100644 index 000000000000..75d77f3606d2 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/SimpleArtifactMetadataSource.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.deployer; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.Collections; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.repository.legacy.metadata.ArtifactMetadataSource; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.apache.maven.repository.legacy.metadata.ResolutionGroup; + +@Named("classpath") +@Singleton +@Deprecated +public class SimpleArtifactMetadataSource implements ArtifactMetadataSource { + @Override + public ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) { + throw new UnsupportedOperationException("Cannot retrieve metadata in this test case"); + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) { + return Collections.singletonList(new DefaultArtifactVersion("10.1.3")); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) { + return Collections.singletonList(new DefaultArtifactVersion("10.1.3")); + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) { + return retrieve(request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/factory/DefaultArtifactFactoryTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/factory/DefaultArtifactFactoryTest.java new file mode 100644 index 000000000000..634e7f7462ef --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/factory/DefaultArtifactFactoryTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.factory; + +import javax.inject.Inject; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.VersionRange; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@PlexusTest +@Deprecated +class DefaultArtifactFactoryTest { + + @Inject + ArtifactFactory factory; + + @Test + void testPropagationOfSystemScopeRegardlessOfInheritedScope() { + Artifact artifact = factory.createDependencyArtifact( + "test-grp", "test-artifact", VersionRange.createFromVersion("1.0"), "type", null, "system", "provided"); + Artifact artifact2 = factory.createDependencyArtifact( + "test-grp", "test-artifact-2", VersionRange.createFromVersion("1.0"), "type", null, "system", "test"); + Artifact artifact3 = factory.createDependencyArtifact( + "test-grp", + "test-artifact-3", + VersionRange.createFromVersion("1.0"), + "type", + null, + "system", + "runtime"); + Artifact artifact4 = factory.createDependencyArtifact( + "test-grp", + "test-artifact-4", + VersionRange.createFromVersion("1.0"), + "type", + null, + "system", + "compile"); + + // this one should never happen in practice... + Artifact artifact5 = factory.createDependencyArtifact( + "test-grp", "test-artifact-5", VersionRange.createFromVersion("1.0"), "type", null, "system", "system"); + + assertEquals("system", artifact.getScope()); + assertEquals("system", artifact2.getScope()); + assertEquals("system", artifact3.getScope()); + assertEquals("system", artifact4.getScope()); + assertEquals("system", artifact5.getScope()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java new file mode 100644 index 000000000000..0d24d73bcbc1 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.installer; + +import javax.inject.Inject; + +import java.io.File; + +import org.apache.maven.artifact.AbstractArtifactComponentTestCase; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.session.scope.internal.SessionScope; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; +import static org.mockito.Mockito.mock; + +/** + */ +@Deprecated +class ArtifactInstallerTest extends AbstractArtifactComponentTestCase { + @Inject + private ArtifactInstaller artifactInstaller; + + @Inject + private SessionScope sessionScope; + + protected String component() { + return "installer"; + } + + @Test + void testArtifactInstallation() throws Exception { + sessionScope.enter(); + try { + sessionScope.seed(MavenSession.class, mock(MavenSession.class)); + + String artifactBasedir = new File(getBasedir(), "src/test/resources/artifact-install").getAbsolutePath(); + + Artifact artifact = createArtifact("artifact", "1.0"); + + File source = new File(artifactBasedir, "artifact-1.0.jar"); + + artifactInstaller.install(source, artifact, localRepository()); + + assertLocalArtifactPresent(artifact); + } finally { + sessionScope.exit(); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/metadata/SwitchableMetadataSource.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/metadata/SwitchableMetadataSource.java new file mode 100644 index 000000000000..1185c3937b59 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/metadata/SwitchableMetadataSource.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.eclipse.sisu.Priority; + +@Singleton +@Priority(10) +@Named +@Deprecated +public class SwitchableMetadataSource implements ArtifactMetadataSource { + private ArtifactMetadataSource delegate; + + @Inject + public SwitchableMetadataSource(@Named("test") ArtifactMetadataSource delegate) { + this.delegate = delegate; + } + + public void setDelegate(ArtifactMetadataSource delegate) { + this.delegate = delegate; + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException { + return delegate.retrieve(request); + } + + @Override + public ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + return delegate.retrieve(artifact, localRepository, remoteRepositories); + } + + @Override + public List retrieveAvailableVersions(MetadataResolutionRequest request) + throws ArtifactMetadataRetrievalException { + return delegate.retrieveAvailableVersions(request); + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + return delegate.retrieveAvailableVersions(artifact, localRepository, remoteRepositories); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws ArtifactMetadataRetrievalException { + return delegate.retrieveAvailableVersionsFromDeploymentRepository(artifact, localRepository, remoteRepository); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/metadata/TestMetadataSource.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/metadata/TestMetadataSource.java new file mode 100644 index 000000000000..dd6e2ef91692 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/metadata/TestMetadataSource.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.metadata; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; + +@Named("test") +@Singleton +@Deprecated +public class TestMetadataSource implements ArtifactMetadataSource { + @Inject + private ArtifactFactory factory; + + @Override + public ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + Set dependencies = new HashSet<>(); + + if ("g".equals(artifact.getArtifactId())) { + Artifact a = null; + try { + a = factory.createBuildArtifact("org.apache.maven", "h", "1.0", "jar"); + dependencies.add(a); + } catch (Exception e) { + throw new ArtifactMetadataRetrievalException("Error retrieving metadata", e, a); + } + } + + if ("i".equals(artifact.getArtifactId())) { + Artifact a = null; + try { + a = factory.createBuildArtifact("org.apache.maven", "j", "1.0-SNAPSHOT", "jar"); + dependencies.add(a); + } catch (Exception e) { + throw new ArtifactMetadataRetrievalException("Error retrieving metadata", e, a); + } + } + + return new ResolutionGroup(artifact, dependencies, remoteRepositories); + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + throw new UnsupportedOperationException("Cannot get available versions in this test case"); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws ArtifactMetadataRetrievalException { + throw new UnsupportedOperationException("Cannot get available versions in this test case"); + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException { + return retrieve(request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } + + @Override + public List retrieveAvailableVersions(MetadataResolutionRequest request) + throws ArtifactMetadataRetrievalException { + return retrieveAvailableVersions( + request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java new file mode 100644 index 000000000000..f529ae822ba4 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.repository; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Deprecated +class MavenArtifactRepositoryTest { + private static class MavenArtifactRepositorySubclass extends MavenArtifactRepository { + String id; + + MavenArtifactRepositorySubclass(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } + } + + @Test + void testHashCodeEquals() { + MavenArtifactRepositorySubclass r1 = new MavenArtifactRepositorySubclass("foo"); + MavenArtifactRepositorySubclass r2 = new MavenArtifactRepositorySubclass("foo"); + MavenArtifactRepositorySubclass r3 = new MavenArtifactRepositorySubclass("bar"); + + assertTrue(r1.hashCode() == r2.hashCode()); + assertFalse(r1.hashCode() == r3.hashCode()); + + assertTrue(r1.equals(r2)); + assertTrue(r2.equals(r1)); + + assertFalse(r1.equals(r3)); + assertFalse(r3.equals(r1)); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/ArtifactResolutionExceptionTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/ArtifactResolutionExceptionTest.java new file mode 100644 index 000000000000..385660370138 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/ArtifactResolutionExceptionTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test the artifact resolution exception message + * + */ +class ArtifactResolutionExceptionTest { + private static final String LS = System.lineSeparator(); + + @Test + void testMissingArtifactMessageFormat() { + String message = "Missing artifact"; + String indentation = " "; + String groupId = "aGroupId"; + String artifactId = "anArtifactId"; + String version = "aVersion"; + String type = "jar"; + String classifier = "aClassifier"; + String downloadUrl = "http://somewhere.com/download"; + List path = Arrays.asList("dependency1", "dependency2"); + String expected = "Missing artifact" + LS + LS + " Try downloading the file manually from: " + LS + + " http://somewhere.com/download" + LS + LS + " Then, install it using the command: " + LS + + " mvn install:install-file -DgroupId=aGroupId -DartifactId=anArtifactId -Dversion=aVersion " + + "-Dclassifier=aClassifier -Dpackaging=jar -Dfile=/path/to/file" + LS + LS + + " Alternatively, if you host your own repository you can deploy the file there: " + LS + + " mvn deploy:deploy-file -DgroupId=aGroupId -DartifactId=anArtifactId" + + " -Dversion=aVersion -Dclassifier=aClassifier -Dpackaging=jar -Dfile=/path/to/file" + + " -Durl=[url] -DrepositoryId=[id]" + LS + LS + " Path to dependency: " + LS + " \t1) dependency1" + + LS + " \t2) dependency2" + LS + LS; + String actual = AbstractArtifactResolutionException.constructMissingArtifactMessage( + message, indentation, groupId, artifactId, version, type, classifier, downloadUrl, path); + assertEquals(expected, actual); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/ArtifactResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/ArtifactResolverTest.java new file mode 100644 index 000000000000..30ade70a919b --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/ArtifactResolverTest.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import javax.inject.Inject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.AbstractArtifactComponentTestCase; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// It would be cool if there was a hook that I could use to set up a test environment. +// I want to set up a local/remote repositories for testing but I don't want to have +// to change them when I change the layout of the repositories. So I want to generate +// the structure I want to test by using the artifact handler manager which dictates +// the layout used for a particular artifact type. + +/** + */ +@Deprecated +class ArtifactResolverTest extends AbstractArtifactComponentTestCase { + @Inject + private ArtifactResolver artifactResolver; + + private Artifact projectArtifact; + + @BeforeEach + @Override + public void setUp() throws Exception { + super.setUp(); + + projectArtifact = createLocalArtifact("project", "3.0"); + } + + @Override + protected DefaultRepositorySystemSession initRepoSession() throws Exception { + DefaultRepositorySystemSession session = super.initRepoSession(); + session.setWorkspaceReader(new TestMavenWorkspaceReader()); + return session; + } + + @Override + protected String component() { + return "resolver"; + } + + @Test + void testResolutionOfASingleArtifactWhereTheArtifactIsPresentInTheLocalRepository() throws Exception { + Artifact a = createLocalArtifact("a", "1.0"); + + artifactResolver.resolve(a, remoteRepositories(), localRepository()); + + assertLocalArtifactPresent(a); + } + + @Test + void testResolutionOfASingleArtifactWhereTheArtifactIsNotPresentLocallyAndMustBeRetrievedFromTheRemoteRepository() + throws Exception { + Artifact b = createRemoteArtifact("b", "1.0-SNAPSHOT"); + deleteLocalArtifact(b); + artifactResolver.resolve(b, remoteRepositories(), localRepository()); + assertLocalArtifactPresent(b); + } + + @Override + protected Artifact createArtifact(String groupId, String artifactId, String version, String type) throws Exception { + // for the anonymous classes + return super.createArtifact(groupId, artifactId, version, type); + } + + @Test + void testTransitiveResolutionWhereAllArtifactsArePresentInTheLocalRepository() throws Exception { + Artifact g = createLocalArtifact("g", "1.0"); + + Artifact h = createLocalArtifact("h", "1.0"); + + ArtifactResolutionResult result = artifactResolver.resolveTransitively( + Collections.singleton(g), projectArtifact, remoteRepositories(), localRepository(), null); + + printErrors(result); + + assertEquals(2, result.getArtifacts().size()); + + assertTrue(result.getArtifacts().contains(g)); + + assertTrue(result.getArtifacts().contains(h)); + + assertLocalArtifactPresent(g); + + assertLocalArtifactPresent(h); + } + + @Test + void + testTransitiveResolutionWhereAllArtifactsAreNotPresentInTheLocalRepositoryAndMustBeRetrievedFromTheRemoteRepository() + throws Exception { + Artifact i = createRemoteArtifact("i", "1.0-SNAPSHOT"); + deleteLocalArtifact(i); + + Artifact j = createRemoteArtifact("j", "1.0-SNAPSHOT"); + deleteLocalArtifact(j); + + ArtifactResolutionResult result = artifactResolver.resolveTransitively( + Collections.singleton(i), projectArtifact, remoteRepositories(), localRepository(), null); + + printErrors(result); + + assertEquals(2, result.getArtifacts().size()); + + assertTrue(result.getArtifacts().contains(i)); + + assertTrue(result.getArtifacts().contains(j)); + + assertLocalArtifactPresent(i); + + assertLocalArtifactPresent(j); + } + + @Test + void testResolutionFailureWhenArtifactNotPresentInRemoteRepository() throws Exception { + Artifact k = createArtifact("k", "1.0"); + + assertThrows( + ArtifactNotFoundException.class, + () -> artifactResolver.resolve(k, remoteRepositories(), localRepository()), + "Resolution succeeded when it should have failed"); + } + + @Test + void testResolutionOfAnArtifactWhereOneRemoteRepositoryIsBadButOneIsGood() throws Exception { + Artifact l = createRemoteArtifact("l", "1.0-SNAPSHOT"); + deleteLocalArtifact(l); + + List repositories = new ArrayList<>(); + repositories.add(remoteRepository()); + repositories.add(badRemoteRepository()); + + artifactResolver.resolve(l, repositories, localRepository()); + + assertLocalArtifactPresent(l); + } + + @Test + public void testReadRepoFromModel() throws Exception { + Artifact artifact = createArtifact(TestMavenWorkspaceReader.ARTIFACT_ID, TestMavenWorkspaceReader.VERSION); + ArtifactMetadataSource source = getContainer().lookup(ArtifactMetadataSource.class, "maven"); + ResolutionGroup group = source.retrieve(artifact, localRepository(), new ArrayList<>()); + List repositories = group.getResolutionRepositories(); + assertEquals(1, repositories.size(), "There should be one repository!"); + ArtifactRepository repository = repositories.get(0); + assertEquals(TestMavenWorkspaceReader.REPO_ID, repository.getId()); + assertEquals(TestMavenWorkspaceReader.REPO_URL, repository.getUrl()); + } + + @Test + void testTransitiveResolutionOrder() throws Exception { + Artifact m = createLocalArtifact("m", "1.0"); + + Artifact n = createLocalArtifact("n", "1.0"); + + ArtifactMetadataSource mds = new ArtifactMetadataSource() { + @Override + public ResolutionGroup retrieve( + Artifact artifact, + ArtifactRepository localRepository, + List remoteRepositories) { + Set dependencies = new HashSet<>(); + + return new ResolutionGroup(artifact, dependencies, remoteRepositories); + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, + ArtifactRepository localRepository, + List remoteRepositories) { + throw new UnsupportedOperationException("Cannot get available versions in this test case"); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) { + throw new UnsupportedOperationException("Cannot get available versions in this test case"); + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) { + return retrieve(request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } + + @Override + public List retrieveAvailableVersions(MetadataResolutionRequest request) { + return retrieveAvailableVersions( + request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } + }; + + ArtifactResolutionResult result = null; + + Set set = new LinkedHashSet<>(); + set.add(n); + set.add(m); + + result = artifactResolver.resolveTransitively( + set, projectArtifact, remoteRepositories(), localRepository(), mds); + + printErrors(result); + + Iterator i = result.getArtifacts().iterator(); + assertEquals(n, i.next(), "n should be first"); + assertEquals(m, i.next(), "m should be second"); + + // inverse order + set = new LinkedHashSet<>(); + set.add(m); + set.add(n); + + result = artifactResolver.resolveTransitively( + set, projectArtifact, remoteRepositories(), localRepository(), mds); + + printErrors(result); + + i = result.getArtifacts().iterator(); + assertEquals(m, i.next(), "m should be first"); + assertEquals(n, i.next(), "n should be second"); + } + + private void printErrors(ArtifactResolutionResult result) { + if (result.hasMissingArtifacts()) { + for (Artifact artifact : result.getMissingArtifacts()) { + System.err.println("Missing: " + artifact); + } + } + + if (result.hasExceptions()) { + for (Exception e : result.getExceptions()) { + e.printStackTrace(); + } + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/DefaultArtifactResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/DefaultArtifactResolverTest.java new file mode 100644 index 000000000000..a523b0d356af --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/DefaultArtifactResolverTest.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import javax.inject.Inject; + +import java.util.Collections; + +import org.apache.maven.artifact.AbstractArtifactComponentTestCase; +import org.apache.maven.artifact.Artifact; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Deprecated +class DefaultArtifactResolverTest extends AbstractArtifactComponentTestCase { + @Inject + private ArtifactResolver artifactResolver; + + private Artifact projectArtifact; + + @BeforeEach + @Override + public void setUp() throws Exception { + super.setUp(); + projectArtifact = createLocalArtifact("project", "3.0"); + } + + @Override + protected String component() { + return "resolver"; + } + + @Test + void testMNG4738() throws Exception { + Artifact g = createLocalArtifact("g", "1.0"); + createLocalArtifact("h", "1.0"); + artifactResolver.resolveTransitively( + Collections.singleton(g), projectArtifact, remoteRepositories(), localRepository(), null); + + // we want to see all top-level thread groups + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + while (tg.getParent() == null) { + tg = tg.getParent(); + } + + ThreadGroup[] tgList = new ThreadGroup[tg.activeGroupCount()]; + tg.enumerate(tgList); + + boolean seen = false; + + for (ThreadGroup aTgList : tgList) { + if (!aTgList.getName().equals(DefaultArtifactResolver.DaemonThreadCreator.THREADGROUP_NAME)) { + continue; + } + + seen = true; + + tg = aTgList; + Thread[] ts = new Thread[tg.activeCount()]; + tg.enumerate(ts); + + for (Thread active : ts) { + String name = active.getName(); + boolean daemon = active.isDaemon(); + assertTrue(daemon, name + " is no daemon Thread."); + } + } + + assertTrue(seen, "Could not find ThreadGroup: " + DefaultArtifactResolver.DaemonThreadCreator.THREADGROUP_NAME); + } + + @Test + void testLookup() throws Exception { + ArtifactResolver resolver = getContainer().lookup(ArtifactResolver.class, "default"); + assertNotNull(resolver); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestFileWagon.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestFileWagon.java new file mode 100644 index 000000000000..f93695376381 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestFileWagon.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.io.File; +import java.io.InputStream; + +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.authorization.AuthorizationException; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.providers.file.FileWagon; +import org.apache.maven.wagon.resource.Resource; + +/** + * Wagon used for test cases that annotate some methods. Note that this is not a thread-safe implementation. + */ +public class TestFileWagon extends FileWagon { + private TestTransferListener testTransferListener; + private boolean insideGet; + + @Deprecated + protected void getTransfer(Resource resource, File destination, InputStream input, boolean closeInput, int maxSize) + throws TransferFailedException { + addTransfer("getTransfer " + resource.getName()); + super.getTransfer(resource, destination, input, closeInput, maxSize); + } + + @Override + public void get(String resourceName, File destination) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException { + addTransfer("get " + resourceName); + + insideGet = true; + + super.get(resourceName, destination); + + insideGet = false; + } + + private void addTransfer(String resourceName) { + if (testTransferListener != null) { + testTransferListener.addTransfer(resourceName); + } + } + + @Override + public boolean getIfNewer(String resourceName, File destination, long timestamp) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException { + if (!insideGet) { + addTransfer("getIfNewer " + resourceName); + } + return super.getIfNewer(resourceName, destination, timestamp); + } + + @Override + public void addTransferListener(TransferListener listener) { + if (listener instanceof TestTransferListener t) { + testTransferListener = t; + } + super.addTransferListener(listener); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestMavenWorkspaceReader.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestMavenWorkspaceReader.java new file mode 100644 index 000000000000..432584fbad6c --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestMavenWorkspaceReader.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Repository; +import org.apache.maven.repository.internal.MavenWorkspaceReader; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.repository.WorkspaceRepository; + +public class TestMavenWorkspaceReader implements MavenWorkspaceReader { + + static final String REPO_LAYOUT = "test"; + + static final String REPO_URL = "https://test/me"; + + static final String REPO_ID = "custom"; + + static final String GROUP_ID = "org.apache.maven"; + + static final String ARTIFACT_ID = "this.is.a.test"; + + static final String VERSION = "99.99"; + + private static final WorkspaceRepository WORKSPACE_REPOSITORY = new WorkspaceRepository(REPO_LAYOUT); + + @Override + public WorkspaceRepository getRepository() { + return WORKSPACE_REPOSITORY; + } + + @Override + public File findArtifact(Artifact artifact) { + return null; + } + + @Override + public List findVersions(Artifact artifact) { + return Collections.emptyList(); + } + + @Override + public Model findModel(Artifact artifact) { + if (GROUP_ID.equals(artifact.getGroupId()) + && ARTIFACT_ID.equals(artifact.getArtifactId()) + && VERSION.equals(artifact.getVersion())) { + Model m = new Model(); + m.setArtifactId(ARTIFACT_ID); + m.setGroupId(GROUP_ID); + m.setVersion(VERSION); + Repository repository = new Repository(); + repository.setId(REPO_ID); + repository.setUrl(REPO_URL); + repository.setLayout(REPO_LAYOUT); + m.getRepositories().add(repository); + return m; + } + return null; + } +} diff --git a/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestTransferListener.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestTransferListener.java similarity index 80% rename from maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestTransferListener.java rename to compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestTransferListener.java index 82ee63e958cf..2709cc53b432 100644 --- a/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestTransferListener.java +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/TestTransferListener.java @@ -1,5 +1,3 @@ -package org.apache.maven.artifact.resolver; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,26 +16,22 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.artifact.resolver; import java.util.ArrayList; import java.util.List; import org.apache.maven.wagon.observers.AbstractTransferListener; -public class TestTransferListener - extends AbstractTransferListener -{ +public class TestTransferListener extends AbstractTransferListener { private final List transfers = new ArrayList<>(); - public List getTransfers() - { + public List getTransfers() { return transfers; } - public void addTransfer( String name ) - { - transfers.add( name ); + public void addTransfer(String name) { + transfers.add(name); } - } diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java new file mode 100644 index 000000000000..fe0343c857cb --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests {@link AndArtifactFilter}. + * + */ +class AndArtifactFilterTest { + + private ArtifactFilter newSubFilter() { + return artifact -> false; + } + + @Test + void testEquals() { + AndArtifactFilter filter1 = new AndArtifactFilter(); + + AndArtifactFilter filter2 = new AndArtifactFilter(Arrays.asList(newSubFilter())); + + assertFalse(filter1.equals(null)); + assertTrue(filter1.equals(filter1)); + assertEquals(filter1.hashCode(), filter1.hashCode()); + + assertFalse(filter1.equals(filter2)); + assertFalse(filter2.equals(filter1)); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java new file mode 100644 index 000000000000..53a26db4ea12 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + */ +class FilterHashEqualsTest { + + @Test + void testIncludesExcludesArtifactFilter() { + List patterns = Arrays.asList("c", "d", "e"); + + IncludesArtifactFilter f1 = new IncludesArtifactFilter(patterns); + + IncludesArtifactFilter f2 = new IncludesArtifactFilter(patterns); + + assertTrue(f1.equals(f2)); + assertTrue(f2.equals(f1)); + assertTrue(f1.hashCode() == f2.hashCode()); + + IncludesArtifactFilter f3 = new IncludesArtifactFilter(Arrays.asList("d", "c", "e")); + assertTrue(f1.equals(f3)); + assertTrue(f1.hashCode() == f3.hashCode()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java new file mode 100644 index 000000000000..6c8cda6802bc --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests {@link OrArtifactFilter}. + * + */ +@Deprecated +class OrArtifactFilterTest { + + private ArtifactFilter newSubFilter() { + return artifact -> false; + } + + @Test + void testEquals() { + OrArtifactFilter filter1 = new OrArtifactFilter(); + + OrArtifactFilter filter2 = new OrArtifactFilter(Arrays.asList(newSubFilter())); + + assertFalse(filter1.equals(null)); + assertTrue(filter1.equals(filter1)); + assertEquals(filter1.hashCode(), filter1.hashCode()); + + assertFalse(filter1.equals(filter2)); + assertFalse(filter2.equals(filter1)); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/ScopeArtifactFilterTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/ScopeArtifactFilterTest.java new file mode 100644 index 000000000000..0addbe8023bd --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/ScopeArtifactFilterTest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.resolver.filter; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests {@link ScopeArtifactFilter}. + * + */ +class ScopeArtifactFilterTest { + + private Artifact newArtifact(String scope) { + return new DefaultArtifact("g", "a", "1.0", scope, "jar", "", null); + } + + @Test + void testIncludeCompile() { + ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_COMPILE); + + assertTrue(filter.include(newArtifact(Artifact.SCOPE_COMPILE))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_SYSTEM))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_PROVIDED))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_RUNTIME))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_TEST))); + } + + @Test + void testIncludeCompilePlusRuntime() { + ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_COMPILE_PLUS_RUNTIME); + + assertTrue(filter.include(newArtifact(Artifact.SCOPE_COMPILE))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_SYSTEM))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_PROVIDED))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_RUNTIME))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_TEST))); + } + + @Test + void testIncludeRuntime() { + ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME); + + assertTrue(filter.include(newArtifact(Artifact.SCOPE_COMPILE))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_SYSTEM))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_PROVIDED))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_RUNTIME))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_TEST))); + } + + @Test + void testIncludeRuntimePlusSystem() { + ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME_PLUS_SYSTEM); + + assertTrue(filter.include(newArtifact(Artifact.SCOPE_COMPILE))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_SYSTEM))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_PROVIDED))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_RUNTIME))); + assertFalse(filter.include(newArtifact(Artifact.SCOPE_TEST))); + } + + @Test + void testIncludeTest() { + ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_TEST); + + assertTrue(filter.include(newArtifact(Artifact.SCOPE_COMPILE))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_SYSTEM))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_PROVIDED))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_RUNTIME))); + assertTrue(filter.include(newArtifact(Artifact.SCOPE_TEST))); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java new file mode 100644 index 000000000000..47372855ca2d --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.testutils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.codehaus.plexus.util.FileUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Deprecated +public class TestFileManager { + + public static final String TEMP_DIR_PATH = System.getProperty("java.io.tmpdir"); + + private List filesToDelete = new ArrayList<>(); + + private final String baseFilename; + + private final String fileSuffix; + + private StackTraceElement callerInfo; + + private Thread cleanupWarning; + + private boolean warnAboutCleanup = false; + + public TestFileManager(String baseFilename, String fileSuffix) { + this.baseFilename = baseFilename; + this.fileSuffix = fileSuffix; + + initializeCleanupMonitoring(); + } + + private void initializeCleanupMonitoring() { + callerInfo = new NullPointerException().getStackTrace()[2]; + + Runnable warning = this::maybeWarnAboutCleanUp; + + cleanupWarning = new Thread(warning); + + Runtime.getRuntime().addShutdownHook(cleanupWarning); + } + + private void maybeWarnAboutCleanUp() { + if (warnAboutCleanup) { + System.out.println("[WARNING] TestFileManager from: " + callerInfo.getClassName() + " not cleaned up!"); + } + } + + public void markForDeletion(File toDelete) { + filesToDelete.add(toDelete); + warnAboutCleanup = true; + } + + public synchronized File createTempDir() { + try { + Thread.sleep(20); + } catch (InterruptedException e) { + // ignore + } + + File dir = new File(TEMP_DIR_PATH, baseFilename + System.currentTimeMillis()); + + dir.mkdirs(); + markForDeletion(dir); + + return dir; + } + + public synchronized File createTempFile() throws IOException { + File tempFile = File.createTempFile(baseFilename, fileSuffix); + tempFile.deleteOnExit(); + markForDeletion(tempFile); + + return tempFile; + } + + public void cleanUp() throws IOException { + for (Iterator it = filesToDelete.iterator(); it.hasNext(); ) { + File file = (File) it.next(); + + if (file.exists()) { + if (file.isDirectory()) { + FileUtils.deleteDirectory(file); + } else { + file.delete(); + } + } + + it.remove(); + } + + warnAboutCleanup = false; + } + + public void assertFileExistence(File dir, String filename, boolean shouldExist) { + File file = new File(dir, filename); + + if (shouldExist) { + assertTrue(file.exists()); + } else { + assertFalse(file.exists()); + } + } + + public void assertFileContents(File dir, String filename, String contentsTest, String encoding) throws IOException { + assertFileExistence(dir, filename, true); + + File file = new File(dir, filename); + + String contents = FileUtils.fileRead(file, encoding); + + assertEquals(contentsTest, contents); + } + + public File createFile(File dir, String filename, String contents, String encoding) throws IOException { + File file = new File(dir, filename); + + file.getParentFile().mkdirs(); + + FileUtils.fileWrite(file.getPath(), encoding, contents); + + markForDeletion(file); + + return file; + } + + public String getFileContents(File file, String encoding) throws IOException { + return FileUtils.fileRead(file, encoding); + } + + protected void finalize() throws Throwable { + maybeWarnAboutCleanUp(); + + super.finalize(); + } + + public File createFile(String filename, String content, String encoding) throws IOException { + File dir = createTempDir(); + return createFile(dir, filename, content, encoding); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/transform/TransformationManagerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/transform/TransformationManagerTest.java new file mode 100644 index 000000000000..4e76243abe66 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/transform/TransformationManagerTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.artifact.transform; + +import javax.inject.Inject; + +import java.util.List; + +import org.apache.maven.repository.legacy.resolver.transform.ArtifactTransformation; +import org.apache.maven.repository.legacy.resolver.transform.ArtifactTransformationManager; +import org.apache.maven.repository.legacy.resolver.transform.LatestArtifactTransformation; +import org.apache.maven.repository.legacy.resolver.transform.ReleaseArtifactTransformation; +import org.apache.maven.repository.legacy.resolver.transform.SnapshotTransformation; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@PlexusTest +@Deprecated +class TransformationManagerTest { + @Inject + ArtifactTransformationManager tm; + + @Test + void testTransformationManager() { + List tms = tm.getArtifactTransformations(); + + assertEquals(3, tms.size()); + + assertTrue( + tms.get(0) instanceof ReleaseArtifactTransformation, + "We expected the release transformation and got " + tms.get(0)); + + assertTrue( + tms.get(1) instanceof LatestArtifactTransformation, + "We expected the latest transformation and got " + tms.get(1)); + + assertTrue( + tms.get(2) instanceof SnapshotTransformation, + "We expected the snapshot transformation and got " + tms.get(2)); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/profiles/manager/DefaultProfileManagerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/profiles/manager/DefaultProfileManagerTest.java new file mode 100644 index 000000000000..d0d798b7e007 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/profiles/manager/DefaultProfileManagerTest.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.profiles.manager; + +import javax.inject.Inject; + +import java.util.List; +import java.util.Properties; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationProperty; +import org.apache.maven.model.Profile; +import org.apache.maven.profiles.DefaultProfileManager; +import org.apache.maven.profiles.ProfileManager; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@PlexusTest +@Deprecated +class DefaultProfileManagerTest { + + @Inject + PlexusContainer container; + + protected PlexusContainer getContainer() { + return container; + } + + @Test + void testShouldActivateDefaultProfile() throws Exception { + Profile notActivated = new Profile(); + notActivated.setId("notActivated"); + + Activation nonActivation = new Activation(); + + nonActivation.setJdk("19.2"); + + notActivated.setActivation(nonActivation); + + Profile defaultActivated = new Profile(); + defaultActivated.setId("defaultActivated"); + + Activation defaultActivation = new Activation(); + + defaultActivation.setActiveByDefault(true); + + defaultActivated.setActivation(defaultActivation); + + Properties props = System.getProperties(); + + ProfileManager profileManager = new DefaultProfileManager(getContainer(), props); + + profileManager.addProfile(notActivated); + profileManager.addProfile(defaultActivated); + + List active = profileManager.getActiveProfiles(); + + assertNotNull(active); + assertEquals(1, active.size()); + assertEquals("defaultActivated", ((Profile) active.get(0)).getId()); + } + + @Test + void testShouldNotActivateDefaultProfile() throws Exception { + Profile syspropActivated = new Profile(); + syspropActivated.setId("syspropActivated"); + + Activation syspropActivation = new Activation(); + + ActivationProperty syspropProperty = new ActivationProperty(); + syspropProperty.setName("java.version"); + + syspropActivation.setProperty(syspropProperty); + + syspropActivated.setActivation(syspropActivation); + + Profile defaultActivated = new Profile(); + defaultActivated.setId("defaultActivated"); + + Activation defaultActivation = new Activation(); + + defaultActivation.setActiveByDefault(true); + + defaultActivated.setActivation(defaultActivation); + + Properties props = System.getProperties(); + + ProfileManager profileManager = new DefaultProfileManager(getContainer(), props); + + profileManager.addProfile(syspropActivated); + profileManager.addProfile(defaultActivated); + + List active = profileManager.getActiveProfiles(); + + assertNotNull(active); + assertEquals(1, active.size()); + assertEquals("syspropActivated", ((Profile) active.get(0)).getId()); + } + + @Test + void testShouldNotActivateReversalOfPresentSystemProperty() throws Exception { + Profile syspropActivated = new Profile(); + syspropActivated.setId("syspropActivated"); + + Activation syspropActivation = new Activation(); + + ActivationProperty syspropProperty = new ActivationProperty(); + syspropProperty.setName("!java.version"); + + syspropActivation.setProperty(syspropProperty); + + syspropActivated.setActivation(syspropActivation); + + Properties props = System.getProperties(); + + ProfileManager profileManager = new DefaultProfileManager(getContainer(), props); + + profileManager.addProfile(syspropActivated); + + List active = profileManager.getActiveProfiles(); + + assertNotNull(active); + assertEquals(0, active.size()); + } + + @Test + void testShouldOverrideAndActivateInactiveProfile() throws Exception { + Profile syspropActivated = new Profile(); + syspropActivated.setId("syspropActivated"); + + Activation syspropActivation = new Activation(); + + ActivationProperty syspropProperty = new ActivationProperty(); + syspropProperty.setName("!java.version"); + + syspropActivation.setProperty(syspropProperty); + + syspropActivated.setActivation(syspropActivation); + + Properties props = System.getProperties(); + + ProfileManager profileManager = new DefaultProfileManager(getContainer(), props); + + profileManager.addProfile(syspropActivated); + + profileManager.explicitlyActivate("syspropActivated"); + + List active = profileManager.getActiveProfiles(); + + assertNotNull(active); + assertEquals(1, active.size()); + assertEquals("syspropActivated", ((Profile) active.get(0)).getId()); + } + + @Test + void testShouldOverrideAndDeactivateActiveProfile() throws Exception { + Profile syspropActivated = new Profile(); + syspropActivated.setId("syspropActivated"); + + Activation syspropActivation = new Activation(); + + ActivationProperty syspropProperty = new ActivationProperty(); + syspropProperty.setName("java.version"); + + syspropActivation.setProperty(syspropProperty); + + syspropActivated.setActivation(syspropActivation); + + Properties props = System.getProperties(); + + ProfileManager profileManager = new DefaultProfileManager(getContainer(), props); + + profileManager.addProfile(syspropActivated); + + profileManager.explicitlyDeactivate("syspropActivated"); + + List active = profileManager.getActiveProfiles(); + + assertNotNull(active); + assertEquals(0, active.size()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java b/compat/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java new file mode 100644 index 000000000000..fcf20ef10501 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Inject; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Arrays; + +import org.apache.maven.api.Session; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.impl.InternalSession; +import org.apache.maven.impl.resolver.scopes.Maven4ScopeManagerConfiguration; +import org.apache.maven.internal.impl.DefaultLookup; +import org.apache.maven.internal.impl.DefaultSession; +import org.apache.maven.internal.impl.InternalMavenSession; +import org.apache.maven.model.building.ModelBuildingException; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.apache.maven.session.scope.internal.SessionScope; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.testing.PlexusTest; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.internal.impl.scope.ScopeManagerImpl; +import org.junit.jupiter.api.BeforeEach; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + */ +@PlexusTest +@Deprecated +public abstract class AbstractMavenProjectTestCase { + protected ProjectBuilder projectBuilder; + + @Inject + protected RepositorySystem repositorySystem; + + @Inject + protected org.eclipse.aether.RepositorySystem resolverRepositorySystem; + + @Inject + protected MavenRepositorySystem mavenRepositorySystem; + + @Inject + protected PlexusContainer container; + + public PlexusContainer getContainer() { + return container; + } + + @BeforeEach + public void setUp() throws Exception { + if (getContainer().hasComponent(ProjectBuilder.class, "test")) { + projectBuilder = getContainer().lookup(ProjectBuilder.class, "test"); + } else { + // default over to the main project builder... + projectBuilder = getContainer().lookup(ProjectBuilder.class); + } + } + + protected ProjectBuilder getProjectBuilder() { + return projectBuilder; + } + + // ---------------------------------------------------------------------- + // Local repository + // ---------------------------------------------------------------------- + + protected File getLocalRepositoryPath() throws FileNotFoundException, URISyntaxException { + File markerFile = getFileForClasspathResource("local-repo/marker.txt"); + + return markerFile.getAbsoluteFile().getParentFile(); + } + + protected static File getFileForClasspathResource(String resource) + throws FileNotFoundException, URISyntaxException { + ClassLoader cloader = Thread.currentThread().getContextClassLoader(); + + URL resourceUrl = cloader.getResource(resource); + + if (resourceUrl == null) { + throw new FileNotFoundException("Unable to find: " + resource); + } + + return new File(resourceUrl.toURI()); + } + + protected ArtifactRepository getLocalRepository() throws Exception { + ArtifactRepositoryLayout repoLayout = getContainer().lookup(ArtifactRepositoryLayout.class); + + ArtifactRepository r = repositorySystem.createArtifactRepository( + "local", "file://" + getLocalRepositoryPath().getAbsolutePath(), repoLayout, null, null); + + return r; + } + + // ---------------------------------------------------------------------- + // Project building + // ---------------------------------------------------------------------- + + protected MavenProject getProjectWithDependencies(File pom) throws Exception { + ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); + configuration.setLocalRepository(getLocalRepository()); + configuration.setRemoteRepositories(Arrays.asList(new ArtifactRepository[] {})); + configuration.setProcessPlugins(true); + configuration.setResolveDependencies(true); + initRepoSession(configuration); + + try { + return projectBuilder.build(pom, configuration).getProject(); + } catch (Exception e) { + Throwable cause = e.getCause(); + if (cause instanceof ModelBuildingException modelBuildingException) { + StringBuilder message = new StringBuilder("In: " + pom + "\n\n"); + for (ModelProblem problem : modelBuildingException.getProblems()) { + message.append(problem).append("\n"); + } + System.out.println(message); + fail(message.toString()); + } + + throw e; + } + } + + protected MavenProject getProject(File pom) throws Exception { + ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); + configuration.setLocalRepository(getLocalRepository()); + initRepoSession(configuration); + + return projectBuilder.build(pom, configuration).getProject(); + } + + protected void initRepoSession(ProjectBuildingRequest request) throws Exception { + File localRepo = new File(request.getLocalRepository().getBasedir()); + DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); + session.setScopeManager(new ScopeManagerImpl(Maven4ScopeManagerConfiguration.INSTANCE)); + session.setLocalRepositoryManager(new LegacyLocalRepositoryManager(localRepo)); + request.setRepositorySession(session); + + DefaultMavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest(); + MavenSession msession = + new MavenSession(getContainer(), session, mavenExecutionRequest, new DefaultMavenExecutionResult()); + DefaultSession iSession = new DefaultSession( + msession, resolverRepositorySystem, null, mavenRepositorySystem, new DefaultLookup(container), null); + InternalSession.associate(session, iSession); + + SessionScope sessionScope = container.lookup(SessionScope.class); + sessionScope.enter(); + sessionScope.seed(MavenSession.class, msession); + sessionScope.seed(InternalMavenSession.class, iSession); + sessionScope.seed(Session.class, iSession); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/ClasspathArtifactResolver.java b/compat/maven-compat/src/test/java/org/apache/maven/project/ClasspathArtifactResolver.java new file mode 100644 index 000000000000..f8c532c6c73a --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/ClasspathArtifactResolver.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.transfer.ArtifactNotFoundException; + +/** + */ +@Named("classpath") +@Singleton +@Deprecated +public class ClasspathArtifactResolver implements ArtifactResolver { + + @Override + public List resolveArtifacts( + RepositorySystemSession session, Collection requests) + throws ArtifactResolutionException { + List results = new ArrayList<>(); + + for (ArtifactRequest request : requests) { + ArtifactResult result = new ArtifactResult(request); + results.add(result); + + Artifact artifact = request.getArtifact(); + if ("maven-test".equals(artifact.getGroupId())) { + String scope = artifact.getArtifactId().substring("scope-".length()); + + try { + artifact = artifact.setFile(ProjectClasspathTestType.getFileForClasspathResource( + ProjectClasspathTestType.DIR + "transitive-" + scope + "-dep.xml")); + result.setArtifact(artifact); + } catch (FileNotFoundException | URISyntaxException e) { + throw new IllegalStateException("Missing test POM for " + artifact, e); + } + } else { + result.addException(new ArtifactNotFoundException(artifact, (RemoteRepository) null)); + throw new ArtifactResolutionException(results); + } + } + + return results; + } + + @Override + public ArtifactResult resolveArtifact(RepositorySystemSession session, ArtifactRequest request) + throws ArtifactResolutionException { + return resolveArtifacts(session, Collections.singleton(request)).get(0); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleBindingsInjector.java b/compat/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleBindingsInjector.java new file mode 100644 index 000000000000..a074bbf6c2e9 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleBindingsInjector.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.Lifecycle; +import org.apache.maven.api.Packaging; +import org.apache.maven.api.Type; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.di.Inject; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Priority; +import org.apache.maven.api.di.Singleton; +import org.apache.maven.api.model.Plugin; +import org.apache.maven.api.model.PluginContainer; +import org.apache.maven.api.model.PluginExecution; +import org.apache.maven.api.services.LifecycleRegistry; +import org.apache.maven.api.services.PackagingRegistry; +import org.apache.maven.impl.model.DefaultLifecycleBindingsInjector; + +import static org.apache.maven.api.Lifecycle.DEFAULT; + +@Singleton +@Named +@Priority(5) +@Deprecated +public class EmptyLifecycleBindingsInjector extends DefaultLifecycleBindingsInjector { + + private static LifecycleRegistry lifecycleRegistry; + private static PackagingRegistry packagingRegistry; + + private static final LifecycleRegistry EMPTY_LIFECYCLE_REGISTRY = new LifecycleRegistry() { + + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public Optional lookup(String id) { + return Optional.empty(); + } + + @Override + public List computePhases(Lifecycle lifecycle) { + return List.of(); + } + }; + + private static final PackagingRegistry EMPTY_PACKAGING_REGISTRY = new PackagingRegistry() { + @Override + public Optional lookup(String id) { + return Optional.of(new Packaging() { + @Override + public String id() { + return id; + } + + @Override + public Type type() { + return null; + } + + @Override + public Map plugins() { + if ("JAR".equals(id)) { + return Map.of( + DEFAULT, + PluginContainer.newBuilder() + .plugins(List.of( + newPlugin("maven-compiler-plugin", "compile", "testCompile"), + newPlugin("maven-resources-plugin", "resources", "testResources"), + newPlugin("maven-surefire-plugin", "test"), + newPlugin("maven-jar-plugin", "jar"), + newPlugin("maven-install-plugin", "install"), + newPlugin("maven-deploy-plugin", "deploy"))) + .build()); + } else { + return Map.of(); + } + } + }); + } + }; + + @Inject + public EmptyLifecycleBindingsInjector(LifecycleRegistry lifecycleRegistry, PackagingRegistry packagingRegistry) { + super(new WrapperLifecycleRegistry(), new WrapperPackagingRegistry()); + EmptyLifecycleBindingsInjector.lifecycleRegistry = lifecycleRegistry; + EmptyLifecycleBindingsInjector.packagingRegistry = packagingRegistry; + } + + public static void useEmpty() { + lifecycleRegistry = EMPTY_LIFECYCLE_REGISTRY; + packagingRegistry = EMPTY_PACKAGING_REGISTRY; + } + + private static Plugin newPlugin(String artifactId, String... goals) { + return Plugin.newBuilder() + .groupId("org.apache.maven.plugins") + .artifactId(artifactId) + .executions(Arrays.stream(goals) + .map(goal -> PluginExecution.newBuilder() + .id("default-" + goal) + .goals(List.of(goal)) + .build()) + .toList()) + .build(); + } + + static class WrapperLifecycleRegistry implements LifecycleRegistry { + @Override + @Nonnull + public Optional lookup(String id) { + return getDelegate().lookup(id); + } + + @Override + public Iterator iterator() { + return getDelegate().iterator(); + } + + protected LifecycleRegistry getDelegate() { + return lifecycleRegistry; + } + + @Override + public List computePhases(Lifecycle lifecycle) { + return List.of(); + } + } + + static class WrapperPackagingRegistry implements PackagingRegistry { + @Override + public Optional lookup(String id) { + return getDelegate().lookup(id); + } + + private PackagingRegistry getDelegate() { + return packagingRegistry; + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java b/compat/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java new file mode 100644 index 000000000000..045de59061be --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.DefaultLifecycles; +import org.apache.maven.lifecycle.LifecycleExecutor; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.plugin.MojoExecution; + +/** + * A stub implementation that assumes an empty lifecycle to bypass interaction with the plugin manager and to avoid + * plugin artifact resolution from repositories. + * + */ +@Deprecated +public class EmptyLifecycleExecutor implements LifecycleExecutor { + + @Override + public MavenExecutionPlan calculateExecutionPlan(MavenSession session, String... tasks) { + return new MavenExecutionPlan(null, new DefaultLifecycles()); + } + + @Override + public MavenExecutionPlan calculateExecutionPlan(MavenSession session, boolean setup, String... tasks) { + return new MavenExecutionPlan(null, new DefaultLifecycles()); + } + + @Override + public void execute(MavenSession session) {} + + @Override + public Set getPluginsBoundByDefaultToAllLifecycles(String packaging) { + Set plugins; + + // NOTE: The upper-case packaging name is intentional, that's a special hinting mode used for certain tests + if ("JAR".equals(packaging)) { + plugins = new LinkedHashSet<>(); + + plugins.add(newPlugin("maven-compiler-plugin", "compile", "testCompile")); + plugins.add(newPlugin("maven-resources-plugin", "resources", "testResources")); + plugins.add(newPlugin("maven-surefire-plugin", "test")); + plugins.add(newPlugin("maven-jar-plugin", "jar")); + plugins.add(newPlugin("maven-install-plugin", "install")); + plugins.add(newPlugin("maven-deploy-plugin", "deploy")); + } else { + plugins = Collections.emptySet(); + } + + return plugins; + } + + private Plugin newPlugin(String artifactId, String... goals) { + Plugin plugin = new Plugin(); + + plugin.setGroupId("org.apache.maven.plugins"); + plugin.setArtifactId(artifactId); + + for (String goal : goals) { + PluginExecution pluginExecution = new PluginExecution(); + pluginExecution.setId("default-" + goal); + pluginExecution.addGoal(goal); + plugin.addExecution(pluginExecution); + } + + return plugin; + } + + @Override + public void calculateForkedExecutions(MojoExecution mojoExecution, MavenSession session) {} + + @Override + public List executeForkedExecutions(MojoExecution mojoExecution, MavenSession session) { + return Collections.emptyList(); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/LegacyLocalRepositoryManager.java b/compat/maven-compat/src/test/java/org/apache/maven/project/LegacyLocalRepositoryManager.java new file mode 100644 index 000000000000..2f42888b5f18 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/LegacyLocalRepositoryManager.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.io.File; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.LocalArtifactRegistration; +import org.eclipse.aether.repository.LocalArtifactRequest; +import org.eclipse.aether.repository.LocalArtifactResult; +import org.eclipse.aether.repository.LocalMetadataRegistration; +import org.eclipse.aether.repository.LocalMetadataRequest; +import org.eclipse.aether.repository.LocalMetadataResult; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.LocalRepositoryManager; +import org.eclipse.aether.repository.RemoteRepository; + +/** + */ +@Deprecated +public class LegacyLocalRepositoryManager implements LocalRepositoryManager { + + private final LocalRepository repository; + + public LegacyLocalRepositoryManager(File basedir) { + this.repository = new LocalRepository(basedir.getAbsoluteFile(), "legacy"); + } + + @Override + public LocalRepository getRepository() { + return repository; + } + + @Override + public String getPathForLocalArtifact(Artifact artifact) { + StringBuilder path = new StringBuilder(128); + + path.append(artifact.getGroupId()).append('/'); + + path.append(artifact.getExtension()).append("s/"); + + path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion()); + + if (!artifact.getClassifier().isEmpty()) { + path.append('-').append(artifact.getClassifier()); + } + + path.append('.').append(artifact.getExtension()); + + return path.toString(); + } + + @Override + public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) { + return getPathForLocalArtifact(artifact); + } + + @Override + public String getPathForLocalMetadata(Metadata metadata) { + return getPath(metadata, "local"); + } + + @Override + public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) { + return getPath(metadata, getRepositoryKey(repository, context)); + } + + String getRepositoryKey(RemoteRepository repository, String context) { + return repository.getId(); + } + + private String getPath(Metadata metadata, String repositoryKey) { + StringBuilder path = new StringBuilder(128); + + if (!metadata.getGroupId().isEmpty()) { + path.append(metadata.getGroupId().replace('.', '/')).append('/'); + + if (!metadata.getArtifactId().isEmpty()) { + path.append(metadata.getArtifactId()).append('/'); + + if (!metadata.getVersion().isEmpty()) { + path.append(metadata.getVersion()).append('/'); + } + } + } + + path.append(insertRepositoryKey(metadata.getType(), repositoryKey)); + + return path.toString(); + } + + private String insertRepositoryKey(String filename, String repositoryKey) { + String result; + int idx = filename.indexOf('.'); + if (idx < 0) { + result = filename + '-' + repositoryKey; + } else { + result = filename.substring(0, idx) + '-' + repositoryKey + filename.substring(idx); + } + return result; + } + + @Override + public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) { + String path = getPathForLocalArtifact(request.getArtifact()); + File file = new File(getRepository().getBasedir(), path); + + LocalArtifactResult result = new LocalArtifactResult(request); + if (file.isFile()) { + result.setFile(file); + result.setAvailable(true); + } + + return result; + } + + @Override + public void add(RepositorySystemSession session, LocalArtifactRegistration request) { + // noop + } + + @Override + public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) { + LocalMetadataResult result = new LocalMetadataResult(request); + + String path; + + Metadata metadata = request.getMetadata(); + String context = request.getContext(); + RemoteRepository remote = request.getRepository(); + + if (remote != null) { + path = getPathForRemoteMetadata(metadata, remote, context); + } else { + path = getPathForLocalMetadata(metadata); + } + + File file = new File(getRepository().getBasedir(), path); + if (file.isFile()) { + result.setFile(file); + } + + return result; + } + + @Override + public void add(RepositorySystemSession session, LocalMetadataRegistration request) { + // noop + } + + @Override + public String toString() { + return String.valueOf(getRepository()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/ProjectClasspathTestType.java b/compat/maven-compat/src/test/java/org/apache/maven/project/ProjectClasspathTestType.java new file mode 100644 index 000000000000..24af8e7dfdc6 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/ProjectClasspathTestType.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import java.io.File; +import java.lang.reflect.Field; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.impl.resolver.DefaultArtifactDescriptorReader; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.ArtifactResolver; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Deprecated +class ProjectClasspathTestType extends AbstractMavenProjectTestCase { + static final String DIR = "projects/scope/"; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + + ArtifactResolver resolver = getContainer().lookup(ArtifactResolver.class, "classpath"); + DefaultArtifactDescriptorReader pomReader = + (DefaultArtifactDescriptorReader) getContainer().lookup(ArtifactDescriptorReader.class); + Field field = DefaultArtifactDescriptorReader.class.getDeclaredField("artifactResolver"); + field.setAccessible(true); + field.set(pomReader, resolver); + + projectBuilder = getContainer().lookup(ProjectBuilder.class, "classpath"); + } + + @Test + void testProjectClasspath() throws Exception { + File f = getFileForClasspathResource(DIR + "project-with-scoped-dependencies.xml"); + + MavenProject project = getProjectWithDependencies(f); + + Artifact artifact; + + assertNotNull(project, "Test project can't be null!"); + + checkArtifactIdScope(project, "provided", "provided"); + checkArtifactIdScope(project, "test", "test"); + checkArtifactIdScope(project, "compile", "compile"); + checkArtifactIdScope(project, "runtime", "runtime"); + checkArtifactIdScope(project, "default", "compile"); + + // check all transitive deps of a test dependency are test, except test and provided which is skipped + artifact = getArtifact(project, "maven-test-test", "scope-provided"); + assertNull(artifact, "Check no provided dependencies are transitive"); + artifact = getArtifact(project, "maven-test-test", "scope-test"); + assertNull(artifact, "Check no test dependencies are transitive"); + + artifact = getArtifact(project, "maven-test-test", "scope-compile"); + assertNotNull(artifact); + + System.out.println("a = " + artifact); + System.out.println("b = " + artifact.getScope()); + assertEquals("test", artifact.getScope(), "Check scope"); + artifact = getArtifact(project, "maven-test-test", "scope-default"); + assertEquals("test", artifact.getScope(), "Check scope"); + artifact = getArtifact(project, "maven-test-test", "scope-runtime"); + assertEquals("test", artifact.getScope(), "Check scope"); + + // check all transitive deps of a provided dependency are provided scope, except for test + checkGroupIdScope(project, "provided", "maven-test-provided"); + artifact = getArtifact(project, "maven-test-provided", "scope-runtime"); + assertEquals("provided", artifact.getScope(), "Check scope"); + + // check all transitive deps of a runtime dependency are runtime scope, except for test + checkGroupIdScope(project, "runtime", "maven-test-runtime"); + artifact = getArtifact(project, "maven-test-runtime", "scope-runtime"); + assertEquals("runtime", artifact.getScope(), "Check scope"); + + // check all transitive deps of a compile dependency are compile scope, except for runtime and test + checkGroupIdScope(project, "compile", "maven-test-compile"); + artifact = getArtifact(project, "maven-test-compile", "scope-runtime"); + assertEquals("runtime", artifact.getScope(), "Check scope"); + + // check all transitive deps of a default dependency are compile scope, except for runtime and test + checkGroupIdScope(project, "compile", "maven-test-default"); + artifact = getArtifact(project, "maven-test-default", "scope-runtime"); + assertEquals("runtime", artifact.getScope(), "Check scope"); + } + + private void checkGroupIdScope(MavenProject project, String scopeValue, String groupId) { + Artifact artifact; + artifact = getArtifact(project, groupId, "scope-compile"); + assertEquals(scopeValue, artifact.getScope(), "Check scope"); + artifact = getArtifact(project, groupId, "scope-test"); + assertNull(artifact, "Check test dependency is not transitive"); + artifact = getArtifact(project, groupId, "scope-provided"); + assertNull(artifact, "Check provided dependency is not transitive"); + artifact = getArtifact(project, groupId, "scope-default"); + assertEquals(scopeValue, artifact.getScope(), "Check scope"); + } + + private void checkArtifactIdScope(MavenProject project, String scope, String scopeValue) { + String artifactId = "scope-" + scope; + Artifact artifact = getArtifact(project, "maven-test", artifactId); + assertNotNull(artifact); + assertEquals(scopeValue, artifact.getScope(), "Check scope"); + } + + private Artifact getArtifact(MavenProject project, String groupId, String artifactId) { + System.out.println("[ Looking for " + groupId + ":" + artifactId + " ]"); + for (Artifact a : project.getArtifacts()) { + System.out.println(a.toString()); + if (artifactId.equals(a.getArtifactId()) && a.getGroupId().equals(groupId)) { + System.out.println("RETURN"); + return a; + } + } + System.out.println("Return null"); + return null; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/TestArtifactResolver.java b/compat/maven-compat/src/test/java/org/apache/maven/project/TestArtifactResolver.java new file mode 100644 index 000000000000..7c71ee046075 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/TestArtifactResolver.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.resolver.DefaultArtifactResolver; + +@Named("classpath") +@Singleton +@Deprecated +public class TestArtifactResolver extends DefaultArtifactResolver { + private ArtifactMetadataSource source; + + @Inject + public TestArtifactResolver(final @Named("classpath") ArtifactMetadataSource source) { + this.source = source; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/TestMavenRepositorySystem.java b/compat/maven-compat/src/test/java/org/apache/maven/project/TestMavenRepositorySystem.java new file mode 100644 index 000000000000..d5769e6b524c --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/TestMavenRepositorySystem.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.repository.legacy.LegacyRepositorySystem; + +@Named("classpath") +@Singleton +@Deprecated +public class TestMavenRepositorySystem extends LegacyRepositorySystem { + @Inject + private ArtifactResolver artifactResolver; +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/TestModelResolver.java b/compat/maven-compat/src/test/java/org/apache/maven/project/TestModelResolver.java new file mode 100644 index 000000000000..ba64b5cbcd7c --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/TestModelResolver.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.impl.resolver.DefaultModelResolver; + +@Named +@Singleton +@Deprecated +public class TestModelResolver extends DefaultModelResolver {} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java b/compat/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java new file mode 100644 index 000000000000..0fc0ba33cc09 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.util.Collections; + +import org.apache.maven.api.services.ModelBuilder; +import org.apache.maven.api.services.model.LifecycleBindingsInjector; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.model.root.RootLocator; +import org.eclipse.aether.RepositorySystem; + +@Named("classpath") +@Singleton +@Deprecated +public class TestProjectBuilder extends DefaultProjectBuilder { + @Inject + public TestProjectBuilder( + ModelBuilder modelBuilder, + ProjectBuildingHelper projectBuildingHelper, + MavenRepositorySystem repositorySystem, + RepositorySystem repoSystem, + ProjectDependenciesResolver dependencyResolver, + RootLocator rootLocator, + LifecycleBindingsInjector lifecycleBindingsInjector) { + super( + modelBuilder, + projectBuildingHelper, + repositorySystem, + repoSystem, + dependencyResolver, + rootLocator, + lifecycleBindingsInjector); + } + + @Override + public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest configuration) + throws ProjectBuildingException { + ProjectBuildingResult result = super.build(pomFile, configuration); + + result.getProject().setRemoteArtifactRepositories(Collections.emptyList()); + + return result; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/artifact/DefaultMavenMetadataCacheTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/artifact/DefaultMavenMetadataCacheTest.java new file mode 100644 index 000000000000..20938963639a --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/artifact/DefaultMavenMetadataCacheTest.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.artifact; + +import java.util.Arrays; +import java.util.Collections; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter; +import org.apache.maven.repository.DelegatingLocalArtifactRepository; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.repository.TestRepositorySystem; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +/** + */ +@Deprecated +class DefaultMavenMetadataCacheTest { + private RepositorySystem repositorySystem; + + @BeforeEach + void setUp() { + repositorySystem = new TestRepositorySystem(); + } + + @Test + void testCacheKey() throws Exception { + Artifact a1 = repositorySystem.createArtifact("testGroup", "testArtifact", "1.2.3", "jar"); + @SuppressWarnings("deprecation") + ArtifactRepository lr1 = new DelegatingLocalArtifactRepository(repositorySystem.createDefaultLocalRepository()); + ArtifactRepository rr1 = repositorySystem.createDefaultRemoteRepository(); + a1.setDependencyFilter(new ExcludesArtifactFilter(Arrays.asList("foo"))); + + Artifact a2 = repositorySystem.createArtifact("testGroup", "testArtifact", "1.2.3", "jar"); + @SuppressWarnings("deprecation") + ArtifactRepository lr2 = new DelegatingLocalArtifactRepository(repositorySystem.createDefaultLocalRepository()); + ArtifactRepository rr2 = repositorySystem.createDefaultRemoteRepository(); + a2.setDependencyFilter(new ExcludesArtifactFilter(Arrays.asList("foo"))); + + // sanity checks + assertNotSame(a1, a2); + assertNotSame(lr1, lr2); + assertNotSame(rr1, rr2); + + DefaultMavenMetadataCache.CacheKey k1 = + new DefaultMavenMetadataCache.CacheKey(a1, false, lr1, Collections.singletonList(rr1)); + DefaultMavenMetadataCache.CacheKey k2 = + new DefaultMavenMetadataCache.CacheKey(a2, false, lr2, Collections.singletonList(rr2)); + + assertEquals(k1.hashCode(), k2.hashCode()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/AbstractProjectInheritanceTestCase.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/AbstractProjectInheritanceTestCase.java new file mode 100644 index 000000000000..7ee78716f1db --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/AbstractProjectInheritanceTestCase.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance; + +import java.io.File; + +import org.apache.maven.project.AbstractMavenProjectTestCase; +import org.apache.maven.project.EmptyLifecycleBindingsInjector; +import org.junit.jupiter.api.BeforeEach; + +import static org.codehaus.plexus.testing.PlexusExtension.getTestFile; + +/** + */ +@Deprecated +public abstract class AbstractProjectInheritanceTestCase extends AbstractMavenProjectTestCase { + protected String getTestSeries() { + String className = getClass().getPackage().getName(); + + return className.substring(className.lastIndexOf('.') + 1); + } + + protected File projectFile(String name) { + return projectFile("maven", name); + } + + protected File projectFile(String groupId, String artifactId) { + return new File(getLocalRepositoryPath(), "/" + groupId + "/poms/" + artifactId + "-1.0.pom"); + } + + // ---------------------------------------------------------------------- + // The local repository for this category of tests + // ---------------------------------------------------------------------- + + protected File getLocalRepositoryPath() { + return getTestFile("target/test-classes/inheritance-repo/" + getTestSeries()); + } + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + EmptyLifecycleBindingsInjector.useEmpty(); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t00/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t00/ProjectInheritanceTest.java new file mode 100644 index 000000000000..88f0ddcc3581 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t00/ProjectInheritanceTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t00; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * A test which demonstrates maven's recursive inheritance where + * a distinct value is taken from each parent contributing to + * the final model of the project being assembled. There is no + * overriding going on amongst the models being used in this test: + * each model in the lineage is providing a value that is not present + * anywhere else in the lineage. We are just making sure that values + * down in the lineage are bubbling up where they should. + * + */ +@Deprecated +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p4 inherits from p3 + // p3 inherits from p2 + // p2 inherits from p1 + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p4 ---> p3 ---> p2 ---> p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testProjectInheritance() throws Exception { + MavenProject p4 = getProject(projectFile("p4")); + + assertEquals("p4", p4.getName()); + + // ---------------------------------------------------------------------- + // Value inherited from p3 + // ---------------------------------------------------------------------- + + assertEquals("2000", p4.getInceptionYear()); + + // ---------------------------------------------------------------------- + // Value taken from p2 + // ---------------------------------------------------------------------- + + assertEquals("mailing-list", p4.getMailingLists().get(0).getName()); + + // ---------------------------------------------------------------------- + // Value taken from p1 + // ---------------------------------------------------------------------- + + assertEquals("scm-url/p2/p3/p4", p4.getScm().getUrl()); + + // ---------------------------------------------------------------------- + // Value taken from p4 + // ---------------------------------------------------------------------- + + assertEquals("Codehaus", p4.getOrganization().getName()); + + // ---------------------------------------------------------------------- + // Value taken from super model + // ---------------------------------------------------------------------- + + assertEquals("4.0.0", p4.getModelVersion()); + + assertEquals("4.0.0", p4.getModelVersion()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t01/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t01/ProjectInheritanceTest.java new file mode 100644 index 000000000000..28a5b945c756 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t01/ProjectInheritanceTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t01; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * A test which demonstrates maven's recursive inheritance where + * we are testing to make sure that elements stated in a model are + * not clobbered by the same elements elsewhere in the lineage. + * + */ +@Deprecated +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p4 inherits from p3 + // p3 inherits from p2 + // p2 inherits from p1 + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p4 ---> p3 ---> p2 ---> p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testProjectInheritance() throws Exception { + // ---------------------------------------------------------------------- + // Check p0 value for org name + // ---------------------------------------------------------------------- + + MavenProject p0 = getProject(projectFile("maven.t01", "p0")); + + assertEquals("p0-org", p0.getOrganization().getName()); + + // ---------------------------------------------------------------------- + // Check p1 value for org name + // ---------------------------------------------------------------------- + + MavenProject p1 = getProject(projectFile("maven.t01", "p1")); + + assertEquals("p1-org", p1.getOrganization().getName()); + + // ---------------------------------------------------------------------- + // Check p2 value for org name + // ---------------------------------------------------------------------- + + MavenProject p2 = getProject(projectFile("maven.t01", "p2")); + + assertEquals("p2-org", p2.getOrganization().getName()); + + // ---------------------------------------------------------------------- + // Check p2 value for org name + // ---------------------------------------------------------------------- + + MavenProject p3 = getProject(projectFile("maven.t01", "p3")); + + assertEquals("p3-org", p3.getOrganization().getName()); + + // ---------------------------------------------------------------------- + // Check p4 value for org name + // ---------------------------------------------------------------------- + + MavenProject p4 = getProject(projectFile("maven.t01", "p4")); + + assertEquals("p4-org", p4.getOrganization().getName()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t02/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t02/ProjectInheritanceTest.java new file mode 100644 index 000000000000..b103b3f660e5 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t02/ProjectInheritanceTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t02; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.model.Build; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.condition.OS.WINDOWS; + +/** + * A test which demonstrates maven's recursive inheritance where + * a distinct value is taken from each parent contributing to + * the final model of the project being assembled. There is no + * overriding going on amongst the models being used in this test: + * each model in the lineage is providing a value that is not present + * anywhere else in the lineage. We are just making sure that values + * down in the lineage are bubbling up where they should. + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p4 inherits from p3 + // p3 inherits from p2 + // p2 inherits from p1 + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p4 ---> p3 ---> p2 ---> p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + @DisabledOnOs(value = WINDOWS, disabledReason = "need to investigate why it fails on windows") + void testProjectInheritance() throws Exception { + File localRepo = getLocalRepositoryPath(); + + System.out.println("Local repository is at: " + localRepo.getAbsolutePath()); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom1 = new File(pom0.getParentFile(), "p1/pom.xml"); + File pom2 = new File(pom1.getParentFile(), "p2/pom.xml"); + File pom3 = new File(pom2.getParentFile(), "p3/pom.xml"); + File pom4 = new File(pom3.getParentFile(), "p4/pom.xml"); + File pom5 = new File(pom4.getParentFile(), "p5/pom.xml"); + + System.out.println("Location of project-4's POM: " + pom4.getPath()); + + // load everything... + MavenProject project0 = getProject(pom0); + MavenProject project1 = getProject(pom1); + MavenProject project2 = getProject(pom2); + MavenProject project3 = getProject(pom3); + MavenProject project4 = getProject(pom4); + MavenProject project5 = getProject(pom5); + + assertEquals("p4", project4.getName()); + + // ---------------------------------------------------------------------- + // Value inherited from p3 + // ---------------------------------------------------------------------- + + assertEquals("2000", project4.getInceptionYear()); + + // ---------------------------------------------------------------------- + // Value taken from p2 + // ---------------------------------------------------------------------- + + assertEquals("mailing-list", project4.getMailingLists().get(0).getName()); + + // ---------------------------------------------------------------------- + // Value taken from p1 + // ---------------------------------------------------------------------- + + assertEquals("scm-url/p2/p3/p4", project4.getScm().getUrl()); + + // ---------------------------------------------------------------------- + // Value taken from p4 + // ---------------------------------------------------------------------- + + assertEquals("Codehaus", project4.getOrganization().getName()); + + // ---------------------------------------------------------------------- + // Value taken from super model + // ---------------------------------------------------------------------- + + assertEquals("4.0.0", project4.getModelVersion()); + + Build build = project4.getBuild(); + List plugins = build.getPlugins(); + + Map validPluginCounts = new HashMap<>(); + + String testPluginArtifactId = "maven-compiler-plugin"; + + // this is the plugin we're looking for. + validPluginCounts.put(testPluginArtifactId, 0); + + // these are injected if -DperformRelease=true + validPluginCounts.put("maven-deploy-plugin", 0); + validPluginCounts.put("maven-javadoc-plugin", 0); + validPluginCounts.put("maven-source-plugin", 0); + + Plugin testPlugin = null; + + for (Plugin plugin : plugins) { + String pluginArtifactId = plugin.getArtifactId(); + + assertTrue(validPluginCounts.containsKey(pluginArtifactId), "Illegal plugin found: " + pluginArtifactId); + + if (pluginArtifactId.equals(testPluginArtifactId)) { + testPlugin = plugin; + } + + Integer count = validPluginCounts.get(pluginArtifactId); + + assertEquals(0, (int) count, "Multiple copies of plugin: " + pluginArtifactId + " found in POM."); + + count = count + 1; + + validPluginCounts.put(pluginArtifactId, count); + } + + assertNotNull(testPlugin); + + List executions = testPlugin.getExecutions(); + + assertEquals(1, executions.size()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t03/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t03/ProjectInheritanceTest.java new file mode 100644 index 000000000000..9ff8e4ac8bcb --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t03/ProjectInheritanceTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t03; + +import java.io.File; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * A test which demonstrates maven's recursive inheritance where + * a distinct value is taken from each parent contributing to + * the final model of the project being assembled. There is no + * overriding going on amongst the models being used in this test: + * each model in the lineage is providing a value that is not present + * anywhere else in the lineage. We are just making sure that values + * down in the lineage are bubbling up where they should. + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testProjectInheritance() throws Exception { + File localRepo = getLocalRepositoryPath(); + File pom0 = new File(localRepo, "p0/pom.xml"); + + File pom0Basedir = pom0.getParentFile(); + + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load everything... + MavenProject project0 = getProject(pom0); + MavenProject project1 = getProject(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java new file mode 100644 index 000000000000..0cbcfc7bfc38 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t04; + +import java.io.File; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Verifies the version of a dependency listed in a parent's + * dependencyManagement section is chosen over another version of the same + * dependency, listed transitively. + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // p1 has a depMgmt section that specifies versions 1.0 of jars "a" & "b" + // jar "a" has a transitive dependency on 2.0 of jar "b", but maven should + // prefer to use version 1.0. + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagementOverridesTransitiveDependencyVersion() throws Exception { + File localRepo = getLocalRepositoryPath(); + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + Set set = project1.getArtifacts(); + assertNotNull(set, "No artifacts"); + assertFalse(set.isEmpty(), "No Artifacts"); + assertTrue(set.size() == 3, "Set size should be 3, is " + set.size()); + + for (Object aSet : set) { + Artifact artifact = (Artifact) aSet; + System.out.println("Artifact: " + artifact.getDependencyConflictId() + " " + artifact.getVersion() + + " Optional=" + (artifact.isOptional() ? "true" : "false")); + assertTrue( + artifact.getVersion().equals("1.0"), "Incorrect version for " + artifact.getDependencyConflictId()); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java new file mode 100644 index 000000000000..c97114514e2c --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t05; + +import java.io.File; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A test which demonstrates maven's dependency management + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagement() throws Exception { + File localRepo = getLocalRepositoryPath(); + File pom0 = new File(localRepo, "p0/pom.xml"); + + File pom0Basedir = pom0.getParentFile(); + + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load everything... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + Set set = project1.getArtifacts(); + assertNotNull(set, "No artifacts"); + assertFalse(set.isEmpty(), "No Artifacts"); + + for (Object aSet : set) { + Artifact artifact = (Artifact) aSet; + System.out.println("Artifact: " + artifact.getDependencyConflictId() + " " + artifact.getVersion() + + " Scope: " + artifact.getScope()); + assertTrue( + artifact.getVersion().equals("1.0"), "Incorrect version for " + artifact.getDependencyConflictId()); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java new file mode 100644 index 000000000000..7dd215988964 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t06; + +import java.io.File; +import java.util.Iterator; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A test which demonstrates maven's dependency management + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagement() throws Exception { + File localRepo = getLocalRepositoryPath(); + File pom0 = new File(localRepo, "p0/pom.xml"); + + File pom0Basedir = pom0.getParentFile(); + + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load everything... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + Set set = project1.getArtifacts(); + assertNotNull(set, "No artifacts"); + assertFalse(set.isEmpty(), "No Artifacts"); + Iterator iter = set.iterator(); + assertTrue(set.size() == 4, "Set size should be 4, is " + set.size()); + + while (iter.hasNext()) { + Artifact artifact = (Artifact) iter.next(); + System.out.println("Artifact: " + artifact.getDependencyConflictId() + " " + artifact.getVersion() + + " Optional=" + (artifact.isOptional() ? "true" : "false")); + assertTrue( + artifact.getVersion().equals("1.0"), "Incorrect version for " + artifact.getDependencyConflictId()); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java new file mode 100644 index 000000000000..a25950845ec1 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t07; + +import java.io.File; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A test which demonstrates maven's dependency management + * + */ +@Deprecated +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagement() throws Exception { + File localRepo = getLocalRepositoryPath(); + File pom0 = new File(localRepo, "p0/pom.xml"); + + File pom0Basedir = pom0.getParentFile(); + + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load everything... + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + System.out.println("Project " + project1.getId() + " " + project1); + Set set = project1.getArtifacts(); + assertNotNull(set, "No artifacts"); + assertFalse(set.isEmpty(), "No Artifacts"); + assertTrue(set.size() == 3, "Set size should be 3, is " + set.size()); + + for (Object aSet : set) { + Artifact artifact = (Artifact) aSet; + assertFalse(artifact.getArtifactId().equals("t07-d")); + System.out.println("Artifact: " + artifact.getDependencyConflictId() + " " + artifact.getVersion() + + " Optional=" + (artifact.isOptional() ? "true" : "false")); + assertTrue( + artifact.getVersion().equals("1.0"), "Incorrect version for " + artifact.getDependencyConflictId()); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java new file mode 100644 index 000000000000..aa5277a7ffd5 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t08; + +import java.io.File; +import java.util.Iterator; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A test which demonstrates maven's dependency management + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagement() throws Exception { + File localRepo = getLocalRepositoryPath(); + File pom0 = new File(localRepo, "p0/pom.xml"); + + File pom0Basedir = pom0.getParentFile(); + + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load everything... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + System.out.println("Project " + project1.getId() + " " + project1); + Set set = project1.getArtifacts(); + assertNotNull(set, "No artifacts"); + assertFalse(set.isEmpty(), "No Artifacts"); + Iterator iter = set.iterator(); + assertTrue(set.size() == 4, "Set size should be 4, is " + set.size()); + + while (iter.hasNext()) { + Artifact artifact = (Artifact) iter.next(); + System.out.println("Artifact: " + artifact.getDependencyConflictId() + " " + artifact.getVersion() + + " Optional=" + (artifact.isOptional() ? "true" : "false")); + assertTrue( + artifact.getVersion().equals("1.0"), "Incorrect version for " + artifact.getDependencyConflictId()); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java new file mode 100644 index 000000000000..7236385f904f --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t09; + +import java.io.File; +import java.util.Map; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Verifies exclusions listed in dependencyManagement are valid for + * transitive dependencies. + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + /** + * How the test project is set up: + * + * 1. dependencyManagement lists dependencies on a & b, + * with an exclusion on c in b. + * 2. the child project lists a dependency on project a only + * 3. a depends on b (which is transitive to the child project), + * and b depends on c. + * + * We should see that the resulting size of collected artifacts is two: + * a & b only. + */ + @Test + void testDependencyManagementExclusionsExcludeTransitively() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertNotNull(project1.getParent(), "Parent is null"); + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + Map map = project1.getArtifactMap(); + + assertNotNull(map, "No artifacts"); + assertFalse(map.isEmpty(), "No Artifacts"); + assertTrue(map.size() == 2, "Set size should be 2, is " + map.size()); + + assertTrue(map.containsKey("maven-test:t09-a"), "maven-test:t09-a is not in the project"); + assertTrue(map.containsKey("maven-test:t09-b"), "maven-test:t09-b is not in the project"); + assertFalse(map.containsKey("maven-test:t09-c"), "maven-test:t09-c is in the project"); + } + + /** + * Setup exactly the same as the above test, except that the child project + * now depends upon d, which has a transitive dependency on c. Even though + * we did list an exclusion on c, it was only from within the context of + * project b. We will pick up project c in this case because no + * restrictions were placed on d. This demonstrates that a, b, c, & d will + * all be collected. + * + * @throws Exception + */ + @Test + void testDependencyManagementExclusionDoesNotOverrideGloballyForTransitives() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom2 = new File(pom0Basedir, "p2/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project2 = getProjectWithDependencies(pom2); + + assertEquals(pom0Basedir, project2.getParent().getBasedir()); + Map map = project2.getArtifactMap(); + assertNotNull(map, "No artifacts"); + assertFalse(map.isEmpty(), "No Artifacts"); + assertTrue(map.size() == 4, "Set size should be 4, is " + map.size()); + + assertTrue(map.containsKey("maven-test:t09-a"), "maven-test:t09-a is not in the project"); + assertTrue(map.containsKey("maven-test:t09-b"), "maven-test:t09-b is not in the project"); + assertTrue(map.containsKey("maven-test:t09-c"), "maven-test:t09-c is not in the project"); + assertTrue(map.containsKey("maven-test:t09-d"), "maven-test:t09-d is not in the project"); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java new file mode 100644 index 000000000000..3fa5698a3bfa --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t10; + +import java.io.File; +import java.util.Map; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Verifies scope inheritance of direct and transitive dependencies. + * + * Should show three behaviors: + * + * 1. dependencyManagement should override the scope of transitive dependencies. + * 2. Direct dependencies should override the scope of dependencyManagement. + * 3. Direct dependencies should inherit scope from dependencyManagement when + * they do not explicitly state a scope. + * + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagementOverridesTransitiveDependencyVersion() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + System.out.println("Project " + project1.getId() + " " + project1); + Map map = project1.getArtifactMap(); + assertNotNull(map, "No artifacts"); + assertFalse(map.isEmpty(), "No Artifacts"); + assertTrue(map.size() == 3, "Set size should be 3, is " + map.size()); + + Artifact a = (Artifact) map.get("maven-test:t10-a"); + Artifact b = (Artifact) map.get("maven-test:t10-b"); + Artifact c = (Artifact) map.get("maven-test:t10-c"); + + assertNotNull(a); + assertNotNull(b); + assertNotNull(c); + + // inherited from depMgmt + System.out.println(a.getScope()); + assertTrue(a.getScope().equals("test"), "Incorrect scope for " + a.getDependencyConflictId()); + + // transitive dep, overridden b depMgmt + assertTrue(b.getScope().equals("runtime"), "Incorrect scope for " + b.getDependencyConflictId()); + + // direct dep, overrides depMgmt + assertTrue(c.getScope().equals("runtime"), "Incorrect scope for " + c.getDependencyConflictId()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t11/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t11/ProjectInheritanceTest.java new file mode 100644 index 000000000000..9b0a188c5666 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t11/ProjectInheritanceTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t11; + +import java.io.File; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Verifies scope of root project is preserved regardless of parent dependency management. + * + * @see MNG-2919 + */ +@Deprecated +@SuppressWarnings("checkstyle:UnusedLocalVariable") +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testDependencyManagementDoesNotOverrideScopeOfCurrentArtifact() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project0 = getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + assertEquals(pom0Basedir, project1.getParent().getBasedir()); + assertNull( + project1.getArtifact().getScope(), + "dependencyManagement has overwritten the scope of the currently building child project"); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t12/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t12/ProjectInheritanceTest.java new file mode 100644 index 000000000000..0e268e4ec31f --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t12/ProjectInheritanceTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t12; + +import java.io.File; +import java.util.Map; + +import org.apache.maven.model.Plugin; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Verifies that plugin execution sections in the parent POM that have + * inherit == false are not inherited to the child POM. + */ +@Deprecated +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testFalsePluginExecutionInheritValue() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom1 = new File(pom0Basedir, "p1/pom.xml"); + + getProjectWithDependencies(pom0); + MavenProject project1 = getProjectWithDependencies(pom1); + + Map pluginMap = project1.getBuild().getPluginsAsMap(); + Plugin compilerPlugin = (Plugin) pluginMap.get("org.apache.maven.plugins:maven-compiler-plugin"); + + assertNotNull(compilerPlugin); + + Map executionMap = compilerPlugin.getExecutionsAsMap(); + assertNull( + executionMap.get("test"), + "Plugin execution: 'test' should NOT exist in the compiler plugin specification for the child project!"); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t12scm/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t12scm/ProjectInheritanceTest.java new file mode 100644 index 000000000000..61c0c1fb0fac --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t12scm/ProjectInheritanceTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.inheritance.t12scm; + +import java.io.File; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Verifies SCM inheritance uses modules statement from parent. + */ +@Deprecated +class ProjectInheritanceTest extends AbstractProjectInheritanceTestCase { + // ---------------------------------------------------------------------- + // + // p1 inherits from p0 + // p0 inherits from super model + // + // or we can show it graphically as: + // + // p1 ---> p0 --> super model + // + // ---------------------------------------------------------------------- + + @Test + void testScmInfoCalculatedCorrectlyOnParentAndChildRead() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom0 = new File(localRepo, "p0/pom.xml"); + File pom0Basedir = pom0.getParentFile(); + File pom1 = new File(pom0Basedir, "modules/p1/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project0 = getProject(pom0); + MavenProject project1 = getProject(pom1); + + assertEquals(project1.getScm().getUrl(), project0.getScm().getUrl() + "/modules/p1"); + assertEquals(project1.getScm().getConnection(), project0.getScm().getConnection() + "/modules/p1"); + assertEquals( + project1.getScm().getDeveloperConnection(), project0.getScm().getDeveloperConnection() + "/modules/p1"); + } + + @Test + void testScmInfoCalculatedCorrectlyOnChildOnlyRead() throws Exception { + File localRepo = getLocalRepositoryPath(); + + File pom1 = new File(localRepo, "p0/modules/p1/pom.xml"); + + // load the child project, which inherits from p0... + MavenProject project1 = getProject(pom1); + + assertEquals("http://host/viewer?path=/p0/modules/p1", project1.getScm().getUrl()); + assertEquals("scm:svn:http://host/p0/modules/p1", project1.getScm().getConnection()); + assertEquals("scm:svn:https://host/p0/modules/p1", project1.getScm().getDeveloperConnection()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/path/DefaultPathTranslatorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/path/DefaultPathTranslatorTest.java new file mode 100644 index 000000000000..d194a78e9c71 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/path/DefaultPathTranslatorTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.project.path; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Deprecated +class DefaultPathTranslatorTest { + + @Test + void testAlignToBasedirWhereBasedirExpressionIsTheCompleteValue() { + File basedir = new File(System.getProperty("java.io.tmpdir"), "test").getAbsoluteFile(); + + String aligned = new DefaultPathTranslator().alignToBaseDirectory("${basedir}", basedir); + + assertEquals(basedir.getAbsolutePath(), aligned); + } + + @Test + void testAlignToBasedirWhereBasedirExpressionIsTheValuePrefix() { + File basedir = new File(System.getProperty("java.io.tmpdir"), "test").getAbsoluteFile(); + + String aligned = new DefaultPathTranslator().alignToBaseDirectory("${basedir}/dir", basedir); + + assertEquals(new File(basedir, "dir").getAbsolutePath(), aligned); + } + + @Test + void testUnalignToBasedirWherePathEqualsBasedir() { + File basedir = new File(System.getProperty("java.io.tmpdir"), "test").getAbsoluteFile(); + + String unaligned = new DefaultPathTranslator().unalignFromBaseDirectory(basedir.getAbsolutePath(), basedir); + + assertEquals(".", unaligned); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java new file mode 100644 index 000000000000..2d2a2631806b --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultArtifactRepository; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +@Deprecated +class DefaultMirrorSelectorTest { + @Test + void testMirrorWithMirrorOfPatternContainingANegationIsNotSelected() { + ArtifactRepository repository = new DefaultArtifactRepository("snapshots.repo", "http://whatever", null); + String pattern = "external:*, !snapshots.repo"; + assertFalse(DefaultMirrorSelector.matchPattern(repository, pattern)); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java new file mode 100644 index 000000000000..8f9709a175c0 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java @@ -0,0 +1,259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import javax.inject.Inject; + +import java.io.File; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.SimpleLookup; +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.services.ArtifactManager; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.SwitchableMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.ResolutionErrorHandler; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.impl.DefaultArtifact; +import org.apache.maven.impl.DefaultArtifactCoordinatesFactory; +import org.apache.maven.impl.DefaultArtifactResolver; +import org.apache.maven.impl.DefaultModelVersionParser; +import org.apache.maven.impl.DefaultRepositoryFactory; +import org.apache.maven.impl.DefaultVersionParser; +import org.apache.maven.impl.InternalSession; +import org.apache.maven.impl.cache.DefaultRequestCacheFactory; +import org.apache.maven.internal.impl.DefaultSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Repository; +import org.apache.maven.model.RepositoryPolicy; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.project.artifact.DefaultMetadataSource; +import org.apache.maven.repository.legacy.LegacyRepositorySystem; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; +import org.codehaus.plexus.testing.PlexusTest; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider; +import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager; +import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer; +import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests {@link LegacyRepositorySystem}. + * + */ +@PlexusTest +@Deprecated +class LegacyRepositorySystemTest { + @Inject + private LegacyRepositorySystem repositorySystem; + + @Inject + private ResolutionErrorHandler resolutionErrorHandler; + + @Inject + private PlexusContainer container; + + @Inject + private org.eclipse.aether.RepositorySystem resolverRepositorySystem; + + protected List getRemoteRepositories() throws Exception { + File repoDir = new File(getBasedir(), "src/test/remote-repo").getAbsoluteFile(); + + RepositoryPolicy policy = new RepositoryPolicy(); + policy.setEnabled(true); + policy.setChecksumPolicy("ignore"); + policy.setUpdatePolicy("always"); + + Repository repository = new Repository(); + repository.setId(RepositorySystem.DEFAULT_REMOTE_REPO_ID); + repository.setUrl("file://" + repoDir.toURI().getPath()); + repository.setReleases(policy); + repository.setSnapshots(policy); + + return Arrays.asList(repositorySystem.buildArtifactRepository(repository)); + } + + protected ArtifactRepository getLocalRepository() throws Exception { + File repoDir = new File(getBasedir(), "target/local-repo").getAbsoluteFile(); + + return repositorySystem.createLocalRepository(repoDir); + } + + @Test + void testThatASystemScopedDependencyIsNotResolvedFromRepositories() throws Exception { + // + // We should get a whole slew of dependencies resolving this artifact transitively + // + Dependency d = new Dependency(); + d.setGroupId("org.apache.maven.its"); + d.setArtifactId("b"); + d.setVersion("0.1"); + d.setScope(Artifact.SCOPE_COMPILE); + Artifact artifact = repositorySystem.createDependencyArtifact(d); + + ArtifactResolutionRequest request = new ArtifactResolutionRequest() + .setArtifact(artifact) + .setResolveRoot(true) + .setResolveTransitively(true) + .setRemoteRepositories(getRemoteRepositories()) + .setLocalRepository(getLocalRepository()); + + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); + LocalRepository localRepo = + new LocalRepository(request.getLocalRepository().getBasedir()); + session.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory().newInstance(session, localRepo)); + LegacySupport legacySupport = container.lookup(LegacySupport.class); + DefaultMavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest(); + MavenSession mavenSession = + new MavenSession(container, session, mavenExecutionRequest, new DefaultMavenExecutionResult()); + legacySupport.setSession(mavenSession); + InternalSession iSession = new DefaultSession( + mavenSession, + resolverRepositorySystem, + null, + null, + new SimpleLookup(List.of( + new DefaultRequestCacheFactory(), + new DefaultRepositoryFactory(new DefaultRemoteRepositoryManager( + new DefaultUpdatePolicyAnalyzer(), new DefaultChecksumPolicyProvider())), + new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme())), + new DefaultArtifactCoordinatesFactory(), + new DefaultArtifactResolver(), + new DefaultRequestCacheFactory(), + new ArtifactManager() { + private final Map paths = new ConcurrentHashMap<>(); + + @Override + public Optional getPath(org.apache.maven.api.Artifact artifact) { + Path path = paths.get(artifact.key()); + if (path == null && artifact instanceof DefaultArtifact defaultArtifact) { + path = defaultArtifact.getArtifact().getPath(); + } + return Optional.ofNullable(path); + } + + @Override + public void setPath(ProducedArtifact artifact, Path path) { + paths.put(artifact.key(), path); + } + })), + null); + InternalSession.associate(session, iSession); + + ArtifactResolutionResult result = repositorySystem.resolve(request); + resolutionErrorHandler.throwErrors(request, result); + assertEquals(2, result.getArtifacts().size()); + + // + // System scoped version which should + // + d.setScope(Artifact.SCOPE_SYSTEM); + File file = new File(getBasedir(), "src/test/repository-system/maven-core-2.1.0.jar"); + assertTrue(file.exists()); + d.setSystemPath(file.getCanonicalPath()); + + artifact = repositorySystem.createDependencyArtifact(d); + + // + // The request has not set any local or remote repositories as the system scoped dependency being resolved + // should only + // give us the dependency off the disk and nothing more. + // + request = new ArtifactResolutionRequest() + .setArtifact(artifact) + .setResolveRoot(true) + .setResolveTransitively(true); + + result = repositorySystem.resolve(request); + resolutionErrorHandler.throwErrors(request, result); + assertEquals(1, result.getArtifacts().size()); + + // + // Put in a bogus file to make sure missing files cause the resolution to fail. + // + file = new File(getBasedir(), "src/test/repository-system/maven-monkey-2.1.0.jar"); + assertFalse(file.exists()); + d.setSystemPath(file.getCanonicalPath()); + artifact = repositorySystem.createDependencyArtifact(d); + + // + // The request has not set any local or remote repositories as the system scoped dependency being resolved + // should only + // give us the dependency off the disk and nothing more. + // + request = new ArtifactResolutionRequest() + .setArtifact(artifact) + .setResolveRoot(true) + .setResolveTransitively(true); + + try { + result = repositorySystem.resolve(request); + resolutionErrorHandler.throwErrors(request, result); + } catch (Exception e) { + assertTrue(result.hasMissingArtifacts()); + } + } + + @Test + void testLocalRepositoryBasedir() throws Exception { + File localRepoDir = new File("").getAbsoluteFile(); + + ArtifactRepository localRepo = repositorySystem.createLocalRepository(localRepoDir); + + String basedir = localRepo.getBasedir(); + + assertFalse(basedir.endsWith("/")); + assertFalse(basedir.endsWith("\\")); + + assertEquals(localRepoDir, new File(basedir)); + + assertEquals(localRepoDir.getPath(), basedir); + } + + @Inject + DefaultMetadataSource defaultMetadataSource; + + @Inject + SwitchableMetadataSource switchableMetadataSource; + + @BeforeEach + void setup() throws CycleDetectedInComponentGraphException { + switchableMetadataSource.setDelegate(defaultMetadataSource); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/MirrorProcessorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/MirrorProcessorTest.java new file mode 100644 index 000000000000..34c859807cb0 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/MirrorProcessorTest.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import javax.inject.Inject; + +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.repository.legacy.repository.ArtifactRepositoryFactory; +import org.apache.maven.settings.Mirror; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@PlexusTest +@Deprecated +class MirrorProcessorTest { + @Inject + private DefaultMirrorSelector mirrorSelector; + + @Inject + private ArtifactRepositoryFactory repositorySystem; + + @Test + void testExternalURL() { + assertTrue(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://somehost"))); + assertTrue(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://somehost:9090/somepath"))); + assertTrue(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "ftp://somehost"))); + assertTrue(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://192.168.101.1"))); + assertTrue(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://"))); + // these are local + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://localhost:8080"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://127.0.0.1:9090"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "file://localhost/somepath"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "file://localhost/D:/somepath"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://localhost"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "http://127.0.0.1"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "file:///somepath"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "file://D:/somepath"))); + + // not a proper url so returns false; + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", "192.168.101.1"))); + assertFalse(DefaultMirrorSelector.isExternalRepo(getRepo("foo", ""))); + } + + @Test + void testMirrorLookup() { + Mirror mirrorA = newMirror("a", "a", "http://a"); + Mirror mirrorB = newMirror("b", "b", "http://b"); + + List mirrors = Arrays.asList(mirrorA, mirrorB); + + assertSame(mirrorA, mirrorSelector.getMirror(getRepo("a", "http://a.a"), mirrors)); + + assertSame(mirrorB, mirrorSelector.getMirror(getRepo("b", "http://a.a"), mirrors)); + + assertNull(mirrorSelector.getMirror(getRepo("c", "http://c.c"), mirrors)); + } + + @Test + void testMirrorWildcardLookup() { + Mirror mirrorA = newMirror("a", "a", "http://a"); + Mirror mirrorB = newMirror("b", "b", "http://b"); + Mirror mirrorC = newMirror("c", "*", "http://wildcard"); + + List mirrors = Arrays.asList(mirrorA, mirrorB, mirrorC); + + assertSame(mirrorA, mirrorSelector.getMirror(getRepo("a", "http://a.a"), mirrors)); + + assertSame(mirrorB, mirrorSelector.getMirror(getRepo("b", "http://a.a"), mirrors)); + + assertSame(mirrorC, mirrorSelector.getMirror(getRepo("c", "http://c.c"), mirrors)); + } + + @Test + void testMirrorStopOnFirstMatch() { + // exact matches win first + Mirror mirrorA2 = newMirror("a2", "a,b", "http://a2"); + Mirror mirrorA = newMirror("a", "a", "http://a"); + // make sure repeated entries are skipped + Mirror mirrorA3 = newMirror("a", "a", "http://a3"); + + Mirror mirrorB = newMirror("b", "b", "http://b"); + Mirror mirrorC = newMirror("c", "d,e", "http://de"); + Mirror mirrorC2 = newMirror("c", "*", "http://wildcard"); + Mirror mirrorC3 = newMirror("c", "e,f", "http://ef"); + + List mirrors = Arrays.asList(mirrorA2, mirrorA, mirrorA3, mirrorB, mirrorC, mirrorC2, mirrorC3); + + assertSame(mirrorA, mirrorSelector.getMirror(getRepo("a", "http://a.a"), mirrors)); + + assertSame(mirrorB, mirrorSelector.getMirror(getRepo("b", "http://a.a"), mirrors)); + + assertSame(mirrorC2, mirrorSelector.getMirror(getRepo("c", "http://c.c"), mirrors)); + + assertSame(mirrorC, mirrorSelector.getMirror(getRepo("d", "http://d"), mirrors)); + + assertSame(mirrorC, mirrorSelector.getMirror(getRepo("e", "http://e"), mirrors)); + + assertSame(mirrorC2, mirrorSelector.getMirror(getRepo("f", "http://f"), mirrors)); + } + + @Test + void testPatterns() { + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "*")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "*,")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), ",*,")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "*,")); + + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "a")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "a,")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), ",a,")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "a,")); + + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("b"), "a")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("b"), "a,")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("b"), ",a")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("b"), ",a,")); + + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "a,b")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("b"), "a,b")); + + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("c"), "a,b")); + + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "*")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "*,b")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a"), "*,!b")); + + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("a"), "*,!a")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("a"), "!a,*")); + + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("c"), "*,!a")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("c"), "!a,*")); + + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("c"), "!a,!c")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("d"), "!a,!c*")); + } + + @Test + void testPatternsWithExternal() { + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a", "http://localhost"), "*")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("a", "http://localhost"), "external:*")); + + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a", "http://localhost"), "external:*,a")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("a", "http://localhost"), "external:*,!a")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("a", "http://localhost"), "a,external:*")); + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("a", "http://localhost"), "!a,external:*")); + + assertFalse(DefaultMirrorSelector.matchPattern(getRepo("c", "http://localhost"), "!a,external:*")); + assertTrue(DefaultMirrorSelector.matchPattern(getRepo("c", "http://somehost"), "!a,external:*")); + } + + @Test + void testLayoutPattern() { + assertTrue(DefaultMirrorSelector.matchesLayout("default", null)); + assertTrue(DefaultMirrorSelector.matchesLayout("default", "")); + assertTrue(DefaultMirrorSelector.matchesLayout("default", "*")); + + assertTrue(DefaultMirrorSelector.matchesLayout("default", "default")); + assertFalse(DefaultMirrorSelector.matchesLayout("default", "legacy")); + + assertTrue(DefaultMirrorSelector.matchesLayout("default", "legacy,default")); + assertTrue(DefaultMirrorSelector.matchesLayout("default", "default,legacy")); + + assertFalse(DefaultMirrorSelector.matchesLayout("default", "legacy,!default")); + assertFalse(DefaultMirrorSelector.matchesLayout("default", "!default,legacy")); + + assertFalse(DefaultMirrorSelector.matchesLayout("default", "*,!default")); + assertFalse(DefaultMirrorSelector.matchesLayout("default", "!default,*")); + } + + @Test + void testMirrorLayoutConsideredForMatching() { + ArtifactRepository repo = getRepo("a"); + + Mirror mirrorA = newMirror("a", "a", null, "http://a"); + Mirror mirrorB = newMirror("b", "a", "p2", "http://b"); + + Mirror mirrorC = newMirror("c", "*", null, "http://c"); + Mirror mirrorD = newMirror("d", "*", "p2", "http://d"); + + assertSame(mirrorA, mirrorSelector.getMirror(repo, Arrays.asList(mirrorA))); + assertNull(mirrorSelector.getMirror(repo, Arrays.asList(mirrorB))); + + assertSame(mirrorC, mirrorSelector.getMirror(repo, Arrays.asList(mirrorC))); + assertNull(mirrorSelector.getMirror(repo, Arrays.asList(mirrorD))); + } + + /** + * Build an ArtifactRepository object. + * + * @param id + * @param url + * @return + */ + private ArtifactRepository getRepo(String id, String url) { + return repositorySystem.createArtifactRepository(id, url, new DefaultRepositoryLayout(), null, null); + } + + /** + * Build an ArtifactRepository object. + * + * @param id + * @return + */ + private ArtifactRepository getRepo(String id) { + return getRepo(id, "http://something"); + } + + private Mirror newMirror(String id, String mirrorOf, String url) { + return newMirror(id, mirrorOf, null, url); + } + + private Mirror newMirror(String id, String mirrorOf, String layouts, String url) { + Mirror mirror = new Mirror(); + + mirror.setId(id); + mirror.setMirrorOf(mirrorOf); + mirror.setMirrorOfLayouts(layouts); + mirror.setUrl(url); + + return mirror; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java new file mode 100644 index 000000000000..60e05adb17d0 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import org.apache.maven.artifact.handler.ArtifactHandler; + +/** + * Assists unit testing. + * + */ +@Deprecated +class TestArtifactHandler implements ArtifactHandler { + + private String type; + + private String extension; + + TestArtifactHandler(String type) { + this(type, type); + } + + TestArtifactHandler(String type, String extension) { + this.type = type; + this.extension = extension; + } + + @Override + public String getClassifier() { + return null; + } + + @Override + public String getDirectory() { + return getPackaging() + "s"; + } + + @Override + public String getExtension() { + return extension; + } + + @Override + public String getLanguage() { + return "java"; + } + + @Override + public String getPackaging() { + return type; + } + + @Override + @Deprecated + public boolean isAddedToClasspath() { + return true; + } + + @Override + public boolean isIncludesDependencies() { + return false; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/TestRepositorySystem.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/TestRepositorySystem.java new file mode 100644 index 000000000000..c2921c3cac98 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/TestRepositorySystem.java @@ -0,0 +1,323 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.api.model.Model; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Repository; +import org.apache.maven.model.io.ModelReader; +import org.apache.maven.project.artifact.ArtifactWithDependencies; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Proxy; +import org.apache.maven.settings.Server; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.sisu.Priority; + +/** + */ +@Named +@Singleton +@Priority(10) +@Deprecated +public class TestRepositorySystem implements RepositorySystem { + + private final ModelReader modelReader; + + private final ArtifactFactory artifactFactory; + + public TestRepositorySystem() { + this(null, null); + } + + @Inject + public TestRepositorySystem(ModelReader modelReader, ArtifactFactory artifactFactory) { + this.modelReader = modelReader; + this.artifactFactory = artifactFactory; + } + + @Override + public ArtifactRepository buildArtifactRepository(Repository repository) throws InvalidRepositoryException { + return new MavenArtifactRepository( + repository.getId(), + repository.getUrl(), + new DefaultRepositoryLayout(), + new ArtifactRepositoryPolicy(), + new ArtifactRepositoryPolicy()); + } + + @Override + public Artifact createArtifact(String groupId, String artifactId, String version, String packaging) { + return createArtifact(groupId, artifactId, version, null, packaging); + } + + @Override + public Artifact createArtifact(String groupId, String artifactId, String version, String scope, String type) { + return new DefaultArtifact(groupId, artifactId, version, scope, type, null, new TestArtifactHandler(type)); + } + + @Override + public ArtifactRepository createArtifactRepository( + String id, + String url, + ArtifactRepositoryLayout repositoryLayout, + ArtifactRepositoryPolicy snapshots, + ArtifactRepositoryPolicy releases) { + return new MavenArtifactRepository(id, url, repositoryLayout, snapshots, releases); + } + + @Override + public Artifact createArtifactWithClassifier( + String groupId, String artifactId, String version, String type, String classifier) { + return new DefaultArtifact(groupId, artifactId, version, null, type, classifier, new TestArtifactHandler(type)); + } + + @Override + public ArtifactRepository createDefaultLocalRepository() throws InvalidRepositoryException { + return createLocalRepository( + new File(System.getProperty("basedir", "."), "target/local-repo").getAbsoluteFile()); + } + + @Override + public ArtifactRepository createDefaultRemoteRepository() throws InvalidRepositoryException { + return new MavenArtifactRepository( + DEFAULT_REMOTE_REPO_ID, + "file://" + + new File(System.getProperty("basedir", "."), "src/test/remote-repo") + .getAbsoluteFile() + .toURI() + .getPath(), + new DefaultRepositoryLayout(), + new ArtifactRepositoryPolicy(), + new ArtifactRepositoryPolicy()); + } + + @Override + public Artifact createDependencyArtifact(Dependency dependency) { + Artifact artifact = new DefaultArtifact( + dependency.getGroupId(), + dependency.getArtifactId(), + dependency.getVersion(), + dependency.getScope(), + dependency.getType(), + dependency.getClassifier(), + new TestArtifactHandler(dependency.getType())); + + if (Artifact.SCOPE_SYSTEM.equals(dependency.getScope())) { + artifact.setFile(new File(dependency.getSystemPath())); + artifact.setResolved(true); + } + + return artifact; + } + + @Override + public ArtifactRepository createLocalRepository(File localRepository) throws InvalidRepositoryException { + return new MavenArtifactRepository( + MavenRepositorySystem.DEFAULT_LOCAL_REPO_ID, + "file://" + localRepository.toURI().getPath(), + new DefaultRepositoryLayout(), + new ArtifactRepositoryPolicy(), + new ArtifactRepositoryPolicy()); + } + + @Override + public Artifact createPluginArtifact(Plugin plugin) { + VersionRange versionRange; + try { + String version = plugin.getVersion(); + if (version == null || version.isEmpty()) { + version = "RELEASE"; + } + versionRange = VersionRange.createFromVersionSpec(version); + } catch (InvalidVersionSpecificationException e) { + return null; + } + + return artifactFactory.createPluginArtifact(plugin.getGroupId(), plugin.getArtifactId(), versionRange); + } + + @Override + public Artifact createProjectArtifact(String groupId, String artifactId, String version) { + return createArtifact(groupId, artifactId, version, "pom"); + } + + @Override + public List getEffectiveRepositories(List repositories) { + return repositories; + } + + @Override + public Mirror getMirror(ArtifactRepository repository, List mirrors) { + return null; + } + + @Override + public void injectAuthentication(List repositories, List servers) {} + + @Override + public void injectMirror(List repositories, List mirrors) {} + + @Override + public void injectProxy(List repositories, List proxies) {} + + @Override + public void publish( + ArtifactRepository repository, File source, String remotePath, ArtifactTransferListener transferListener) + throws ArtifactTransferFailedException { + // TODO Auto-generated method stub + + } + + @Override + public ArtifactResolutionResult resolve(ArtifactResolutionRequest request) { + ArtifactResolutionResult result = new ArtifactResolutionResult(); + + if (request.isResolveRoot()) { + try { + resolve(request.getArtifact(), request); + result.addArtifact(request.getArtifact()); + } catch (IOException e) { + result.addMissingArtifact(request.getArtifact()); + } + } + + if (request.isResolveTransitively()) { + Map artifacts = new LinkedHashMap<>(); + + if (request.getArtifactDependencies() != null) { + for (Artifact artifact : request.getArtifactDependencies()) { + artifacts.put(artifact.getDependencyConflictId(), artifact); + } + } + + List dependencies = new ArrayList<>(); + if (request.getArtifact() instanceof ArtifactWithDependencies artifactWithDependencies) { + dependencies = artifactWithDependencies.getDependencies(); + } else { + Artifact pomArtifact = createProjectArtifact( + request.getArtifact().getGroupId(), + request.getArtifact().getArtifactId(), + request.getArtifact().getVersion()); + File pomFile = new File( + request.getLocalRepository().getBasedir(), + request.getLocalRepository().pathOf(pomArtifact)); + + try { + Model model = modelReader.read(pomFile, null).getDelegate(); + + dependencies = Dependency.dependencyToApiV3(model.getDependencies()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + for (Dependency dependency : dependencies) { + Artifact artifact = createDependencyArtifact(dependency); + if (!artifacts.containsKey(artifact.getDependencyConflictId())) { + artifacts.put(artifact.getDependencyConflictId(), artifact); + } + } + + for (Artifact artifact : artifacts.values()) { + try { + resolve(artifact, request); + result.addArtifact(artifact); + } catch (IOException e) { + result.addMissingArtifact(artifact); + } + } + } + + return result; + } + + private void resolve(Artifact artifact, ArtifactResolutionRequest request) throws IOException { + if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) { + return; + } + + ArtifactRepository localRepo = request.getLocalRepository(); + + File localFile = new File(localRepo.getBasedir(), localRepo.pathOf(artifact)); + + artifact.setFile(localFile); + + if (!localFile.exists()) { + if (request.getRemoteRepositories().isEmpty()) { + throw new IOException(localFile + " does not exist and no remote repositories are configured"); + } + + ArtifactRepository remoteRepo = request.getRemoteRepositories().get(0); + + File remoteFile = new File(remoteRepo.getBasedir(), remoteRepo.pathOf(artifact)); + + Files.createDirectories(localFile.toPath().getParent()); + Files.copy(remoteFile.toPath(), localFile.toPath()); + } + + artifact.setResolved(true); + } + + @Override + public void retrieve( + ArtifactRepository repository, + File destination, + String remotePath, + ArtifactTransferListener transferListener) + throws ArtifactTransferFailedException, ArtifactDoesNotExistException { + // TODO Auto-generated method stub + + } + + @Override + public void injectMirror(RepositorySystemSession session, List repositories) {} + + @Override + public void injectProxy(RepositorySystemSession session, List repositories) {} + + @Override + public void injectAuthentication(RepositorySystemSession session, List repositories) {} +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java new file mode 100644 index 000000000000..e06318c1db31 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Inject; + +import java.io.File; + +import org.apache.maven.artifact.AbstractArtifactComponentTestCase; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; +import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Deprecated +class DefaultUpdateCheckManagerTest extends AbstractArtifactComponentTestCase { + + @Inject + private ArtifactFactory artifactFactory; + + DefaultUpdateCheckManager updateCheckManager; + + @Override + protected String component() { + return "updateCheckManager"; + } + + @BeforeEach + @Override + public void setUp() throws Exception { + super.setUp(); + + updateCheckManager = new DefaultUpdateCheckManager(new ConsoleLogger(Logger.LEVEL_DEBUG, "test")); + } + + @Test + void testArtifact() throws Exception { + ArtifactRepository remoteRepository = remoteRepository(); + + ArtifactRepository localRepository = localRepository(); + + Artifact a = createArtifact("a", "0.0.1-SNAPSHOT"); + File file = new File(localRepository.getBasedir(), localRepository.pathOf(a)); + file.delete(); + a.setFile(file); + + File touchFile = updateCheckManager.getTouchfile(a); + touchFile.delete(); + + assertTrue(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + file.getParentFile().mkdirs(); + file.createNewFile(); + updateCheckManager.touch(a, remoteRepository, null); + + assertFalse(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + assertNull( + updateCheckManager.readLastUpdated(touchFile, updateCheckManager.getRepositoryKey(remoteRepository))); + + assertFalse(updateCheckManager.getTouchfile(a).exists()); + } + + @Test + void testMissingArtifact() throws Exception { + ArtifactRepository remoteRepository = remoteRepository(); + + ArtifactRepository localRepository = localRepository(); + + Artifact a = createArtifact("a", "0.0.1-SNAPSHOT"); + File file = new File(localRepository.getBasedir(), localRepository.pathOf(a)); + file.delete(); + a.setFile(file); + + File touchFile = updateCheckManager.getTouchfile(a); + touchFile.delete(); + + assertTrue(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + updateCheckManager.touch(a, remoteRepository, null); + + assertFalse(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + assertFalse(file.exists()); + assertNotNull( + updateCheckManager.readLastUpdated(touchFile, updateCheckManager.getRepositoryKey(remoteRepository))); + } + + @Test + void testPom() throws Exception { + ArtifactRepository remoteRepository = remoteRepository(); + + ArtifactRepository localRepository = localRepository(); + + Artifact a = createArtifact("a", "0.0.1", "pom"); + File file = new File(localRepository.getBasedir(), localRepository.pathOf(a)); + file.delete(); + a.setFile(file); + + File touchFile = updateCheckManager.getTouchfile(a); + touchFile.delete(); + + assertTrue(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + file.getParentFile().mkdirs(); + file.createNewFile(); + updateCheckManager.touch(a, remoteRepository, null); + + assertFalse(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + assertNull( + updateCheckManager.readLastUpdated(touchFile, updateCheckManager.getRepositoryKey(remoteRepository))); + + assertFalse(updateCheckManager.getTouchfile(a).exists()); + } + + @Test + void testMissingPom() throws Exception { + ArtifactRepository remoteRepository = remoteRepository(); + + ArtifactRepository localRepository = localRepository(); + + Artifact a = createArtifact("a", "0.0.1", "pom"); + File file = new File(localRepository.getBasedir(), localRepository.pathOf(a)); + file.delete(); + a.setFile(file); + + File touchFile = updateCheckManager.getTouchfile(a); + touchFile.delete(); + + assertTrue(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + updateCheckManager.touch(a, remoteRepository, null); + + assertFalse(updateCheckManager.isUpdateRequired(a, remoteRepository)); + + assertFalse(file.exists()); + assertNotNull( + updateCheckManager.readLastUpdated(touchFile, updateCheckManager.getRepositoryKey(remoteRepository))); + } + + @Test + void testMetadata() throws Exception { + ArtifactRepository remoteRepository = remoteRepository(); + + ArtifactRepository localRepository = localRepository(); + + Artifact a = createRemoteArtifact("a", "0.0.1-SNAPSHOT"); + RepositoryMetadata metadata = new ArtifactRepositoryMetadata(a); + + File file = new File( + localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata(metadata, localRepository)); + file.delete(); + + File touchFile = updateCheckManager.getTouchfile(metadata, file); + touchFile.delete(); + + assertTrue(updateCheckManager.isUpdateRequired(metadata, remoteRepository, file)); + + file.getParentFile().mkdirs(); + file.createNewFile(); + updateCheckManager.touch(metadata, remoteRepository, file); + + assertFalse(updateCheckManager.isUpdateRequired(metadata, remoteRepository, file)); + + assertNotNull(updateCheckManager.readLastUpdated( + touchFile, updateCheckManager.getMetadataKey(remoteRepository, file))); + } + + @Test + void testMissingMetadata() throws Exception { + ArtifactRepository remoteRepository = remoteRepository(); + + ArtifactRepository localRepository = localRepository(); + + Artifact a = createRemoteArtifact("a", "0.0.1-SNAPSHOT"); + RepositoryMetadata metadata = new ArtifactRepositoryMetadata(a); + + File file = new File( + localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata(metadata, localRepository)); + file.delete(); + + File touchFile = updateCheckManager.getTouchfile(metadata, file); + touchFile.delete(); + + assertTrue(updateCheckManager.isUpdateRequired(metadata, remoteRepository, file)); + + updateCheckManager.touch(metadata, remoteRepository, file); + + assertFalse(updateCheckManager.isUpdateRequired(metadata, remoteRepository, file)); + + assertNotNull(updateCheckManager.readLastUpdated( + touchFile, updateCheckManager.getMetadataKey(remoteRepository, file))); + } + + @Test + void testArtifactTouchFileName() throws Exception { + ArtifactRepository localRepository = localRepository(); + + Artifact a = artifactFactory.createArtifactWithClassifier("groupId", "a", "0.0.1-SNAPSHOT", "jar", null); + File file = new File(localRepository.getBasedir(), localRepository.pathOf(a)); + a.setFile(file); + + assertEquals( + "a-0.0.1-SNAPSHOT.jar.lastUpdated", + updateCheckManager.getTouchfile(a).getName()); + + a = artifactFactory.createArtifactWithClassifier("groupId", "a", "0.0.1-SNAPSHOT", "jar", "classifier"); + file = new File(localRepository.getBasedir(), localRepository.pathOf(a)); + a.setFile(file); + + assertEquals( + "a-0.0.1-SNAPSHOT-classifier.jar.lastUpdated", + updateCheckManager.getTouchfile(a).getName()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultWagonManagerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultWagonManagerTest.java new file mode 100644 index 000000000000..903a0f81b792 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultWagonManagerTest.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Inject; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadata; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.repository.legacy.repository.ArtifactRepositoryFactory; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.UnsupportedProtocolException; +import org.apache.maven.wagon.Wagon; +import org.apache.maven.wagon.events.TransferEvent; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.observers.AbstractTransferListener; +import org.apache.maven.wagon.observers.Debug; +import org.codehaus.plexus.testing.PlexusTest; +import org.codehaus.plexus.util.FileUtils; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getTestFile; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + */ +@PlexusTest +@Deprecated +class DefaultWagonManagerTest { + @Inject + private WagonManager wagonManager; + + private final TransferListener transferListener = new Debug(); + + @Inject + private ArtifactFactory artifactFactory; + + @Inject + private ArtifactRepositoryFactory artifactRepositoryFactory; + + @Test + void testUnnecessaryRepositoryLookup() throws Exception { + Artifact artifact = createTestPomArtifact("target/test-data/get-missing-pom"); + + List repos = new ArrayList<>(); + repos.add(artifactRepositoryFactory.createArtifactRepository( + "repo1", "string://url1", new ArtifactRepositoryLayoutStub(), null, null)); + repos.add(artifactRepositoryFactory.createArtifactRepository( + "repo2", "string://url2", new ArtifactRepositoryLayoutStub(), null, null)); + + StringWagon wagon = (StringWagon) wagonManager.getWagon("string"); + wagon.addExpectedContent(repos.get(0).getLayout().pathOf(artifact), "expected"); + wagon.addExpectedContent( + repos.get(0).getLayout().pathOf(artifact) + ".md5", "cd26d9e10ce691cc69aa2b90dcebbdac"); + wagon.addExpectedContent(repos.get(1).getLayout().pathOf(artifact), "expected"); + wagon.addExpectedContent( + repos.get(1).getLayout().pathOf(artifact) + ".md5", "cd26d9e10ce691cc69aa2b90dcebbdac"); + + class TransferListener extends AbstractTransferListener { + List events = new ArrayList<>(); + + @Override + public void transferInitiated(TransferEvent transferEvent) { + events.add(transferEvent); + } + } + + TransferListener listener = new TransferListener(); + wagonManager.getArtifact(artifact, repos, listener, false); + assertEquals(1, listener.events.size()); + } + + @Test + void testGetMissingJar() throws TransferFailedException, UnsupportedProtocolException, IOException { + Artifact artifact = createTestArtifact("target/test-data/get-missing-jar", "jar"); + + ArtifactRepository repo = createStringRepo(); + + assertThrows(ResourceDoesNotExistException.class, () -> wagonManager.getArtifact(artifact, repo, null, false)); + + assertFalse(artifact.getFile().exists()); + } + + @Test + void testGetMissingJarForced() throws TransferFailedException, UnsupportedProtocolException, IOException { + Artifact artifact = createTestArtifact("target/test-data/get-missing-jar", "jar"); + + ArtifactRepository repo = createStringRepo(); + + assertThrows(ResourceDoesNotExistException.class, () -> wagonManager.getArtifact(artifact, repo, null, true)); + + assertFalse(artifact.getFile().exists()); + } + + @Test + void testGetRemoteJar() + throws TransferFailedException, ResourceDoesNotExistException, UnsupportedProtocolException, IOException { + Artifact artifact = createTestArtifact("target/test-data/get-remote-jar", "jar"); + + ArtifactRepository repo = createStringRepo(); + + StringWagon wagon = (StringWagon) wagonManager.getWagon("string"); + wagon.addExpectedContent(repo.getLayout().pathOf(artifact), "expected"); + wagon.addExpectedContent(repo.getLayout().pathOf(artifact) + ".md5", "cd26d9e10ce691cc69aa2b90dcebbdac"); + + wagonManager.getArtifact(artifact, repo, null, false); + + assertTrue(artifact.getFile().exists()); + assertEquals("expected", FileUtils.fileRead(artifact.getFile(), "UTF-8")); + } + + private Artifact createTestPomArtifact(String directory) throws IOException { + File testData = getTestFile(directory); + FileUtils.deleteDirectory(testData); + testData.mkdirs(); + + Artifact artifact = artifactFactory.createProjectArtifact("test", "test", "1.0"); + artifact.setFile(new File(testData, "test-1.0.pom")); + assertFalse(artifact.getFile().exists()); + return artifact; + } + + private Artifact createTestArtifact(String directory, String type) throws IOException { + return createTestArtifact(directory, "1.0", type); + } + + private Artifact createTestArtifact(String directory, String version, String type) throws IOException { + File testData = getTestFile(directory); + FileUtils.deleteDirectory(testData); + testData.mkdirs(); + + Artifact artifact = artifactFactory.createBuildArtifact("test", "test", version, type); + artifact.setFile(new File( + testData, + "test-" + version + "." + artifact.getArtifactHandler().getExtension())); + assertFalse(artifact.getFile().exists()); + return artifact; + } + + private ArtifactRepository createStringRepo() { + return artifactRepositoryFactory.createArtifactRepository( + "id", "string://url", new ArtifactRepositoryLayoutStub(), null, null); + } + + @Test + void testDefaultWagonManager() throws Exception { + assertWagon("a"); + + assertWagon("b"); + + assertWagon("c"); + + assertWagon("string"); + + assertThrows(UnsupportedProtocolException.class, () -> assertWagon("d")); + } + + /** + * Check that transfer listeners are properly removed after getArtifact and putArtifact + */ + @Test + void testWagonTransferListenerRemovedAfterGetArtifactAndPutArtifact() throws Exception { + Artifact artifact = createTestArtifact("target/test-data/transfer-listener", "jar"); + ArtifactRepository repo = createStringRepo(); + StringWagon wagon = (StringWagon) wagonManager.getWagon("string"); + wagon.addExpectedContent(repo.getLayout().pathOf(artifact), "expected"); + wagon.addExpectedContent(repo.getLayout().pathOf(artifact) + ".md5", "cd26d9e10ce691cc69aa2b90dcebbdac"); + + /* getArtifact */ + assertFalse( + wagon.getTransferEventSupport().hasTransferListener(transferListener), + "Transfer listener is registered before test"); + wagonManager.getArtifact(artifact, repo, transferListener, false); + assertFalse( + wagon.getTransferEventSupport().hasTransferListener(transferListener), + "Transfer listener still registered after getArtifact"); + + /* putArtifact */ + File sampleFile = getTestFile("target/test-file"); + FileUtils.fileWrite(sampleFile.getAbsolutePath(), "sample file"); + + assertFalse( + wagon.getTransferEventSupport().hasTransferListener(transferListener), + "Transfer listener is registered before test"); + wagonManager.putArtifact(sampleFile, artifact, repo, transferListener); + assertFalse( + wagon.getTransferEventSupport().hasTransferListener(transferListener), + "Transfer listener still registered after putArtifact"); + } + + /** + * Checks the verification of checksums. + */ + @Disabled + @Test + void testChecksumVerification() throws Exception { + ArtifactRepositoryPolicy policy = new ArtifactRepositoryPolicy( + true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL); + + ArtifactRepository repo = artifactRepositoryFactory.createArtifactRepository( + "id", "string://url", new ArtifactRepositoryLayoutStub(), policy, policy); + + Artifact artifact = new DefaultArtifact( + "sample.group", + "sample-art", + VersionRange.createFromVersion("1.0"), + "scope", + "jar", + "classifier", + null); + artifact.setFile(getTestFile("target/sample-art")); + + StringWagon wagon = (StringWagon) wagonManager.getWagon("string"); + + wagon.clearExpectedContent(); + wagon.addExpectedContent("path", "lower-case-checksum"); + wagon.addExpectedContent("path.sha1", "2a25dc564a3b34f68237fc849066cbc7bb7a36a1"); + wagonManager.getArtifact(artifact, repo, null, false); + + wagon.clearExpectedContent(); + wagon.addExpectedContent("path", "upper-case-checksum"); + wagon.addExpectedContent("path.sha1", "B7BB97D7D0B9244398D9B47296907F73313663E6"); + wagonManager.getArtifact(artifact, repo, null, false); + + wagon.clearExpectedContent(); + wagon.addExpectedContent("path", "expected-failure"); + wagon.addExpectedContent("path.sha1", "b7bb97d7d0b9244398d9b47296907f73313663e6"); + assertThrows( + ChecksumFailedException.class, + () -> wagonManager.getArtifact(artifact, repo, null, false), + "Checksum verification did not fail"); + + wagon.clearExpectedContent(); + wagon.addExpectedContent("path", "lower-case-checksum"); + wagon.addExpectedContent("path.md5", "50b2cf50a103a965efac62b983035cac"); + wagonManager.getArtifact(artifact, repo, null, false); + + wagon.clearExpectedContent(); + wagon.addExpectedContent("path", "upper-case-checksum"); + wagon.addExpectedContent("path.md5", "842F568FCCFEB7E534DC72133D42FFDC"); + wagonManager.getArtifact(artifact, repo, null, false); + + wagon.clearExpectedContent(); + wagon.addExpectedContent("path", "expected-failure"); + wagon.addExpectedContent("path.md5", "b7bb97d7d0b9244398d9b47296907f73313663e6"); + assertThrows( + ChecksumFailedException.class, + () -> wagonManager.getArtifact(artifact, repo, null, false), + "Checksum verification did not fail"); + } + + @Test + void testPerLookupInstantiation() throws Exception { + String protocol = "perlookup"; + + Wagon one = wagonManager.getWagon(protocol); + Wagon two = wagonManager.getWagon(protocol); + + assertNotSame(one, two); + } + + private void assertWagon(String protocol) throws Exception { + Wagon wagon = wagonManager.getWagon(protocol); + + assertNotNull(wagon, "Check wagon, protocol=" + protocol); + } + + private final class ArtifactRepositoryLayoutStub implements ArtifactRepositoryLayout { + @Override + public String getId() { + return "test"; + } + + @Override + public String pathOfRemoteRepositoryMetadata(ArtifactMetadata metadata) { + return "path"; + } + + @Override + public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) { + return "path"; + } + + @Override + public String pathOf(Artifact artifact) { + return "path"; + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/LegacyRepositorySystemTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/LegacyRepositorySystemTest.java new file mode 100644 index 000000000000..82554ea83ac7 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/LegacyRepositorySystemTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Inject; + +import java.io.File; +import java.util.Arrays; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.Authentication; +import org.apache.maven.settings.Server; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Tests {@link LegacyRepositorySystem}. + * + */ +@PlexusTest +@Deprecated +class LegacyRepositorySystemTest { + @Inject + private LegacyRepositorySystem repositorySystem; + + @Test + void testThatLocalRepositoryWithSpacesIsProperlyHandled() throws Exception { + File basedir = new File("target/spacy path").getAbsoluteFile(); + ArtifactRepository repo = repositorySystem.createLocalRepository(basedir); + assertEquals(basedir, new File(repo.getBasedir())); + } + + @Test + void testAuthenticationHandling() { + Server server = new Server(); + server.setId("repository"); + server.setUsername("jason"); + server.setPassword("abc123"); + + ArtifactRepository repository = + repositorySystem.createArtifactRepository("repository", "http://foo", null, null, null); + repositorySystem.injectAuthentication(Arrays.asList(repository), Arrays.asList(server)); + Authentication authentication = repository.getAuthentication(); + assertNotNull(authentication); + assertEquals("jason", authentication.getUsername()); + assertEquals("abc123", authentication.getPassword()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/PerLookupWagon.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/PerLookupWagon.java new file mode 100644 index 000000000000..9d4b5d3e0d30 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/PerLookupWagon.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Named; + +/** + * Wagon with per-lookup instantiation strategy. + */ +@Named("perlookup") +public class PerLookupWagon extends WagonMock { + + public String[] getSupportedProtocols() { + return new String[] {"perlookup"}; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/StringWagon.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/StringWagon.java new file mode 100644 index 000000000000..6e32a115dc55 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/StringWagon.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.wagon.ConnectionException; +import org.apache.maven.wagon.InputData; +import org.apache.maven.wagon.OutputData; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.StreamWagon; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.authentication.AuthenticationException; +import org.apache.maven.wagon.authorization.AuthorizationException; +import org.apache.maven.wagon.resource.Resource; + +@Named("string") +@Singleton +public class StringWagon extends StreamWagon { + private Map expectedContent = new HashMap<>(); + + public void addExpectedContent(String resourceName, String expectedContent) { + this.expectedContent.put(resourceName, expectedContent); + } + + public String[] getSupportedProtocols() { + return new String[] {"string"}; + } + + @Override + public void closeConnection() throws ConnectionException {} + + @Override + public void fillInputData(InputData inputData) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException { + Resource resource = inputData.getResource(); + + String content = expectedContent.get(resource.getName()); + + if (content != null) { + resource.setContentLength(content.length()); + resource.setLastModified(System.currentTimeMillis()); + + inputData.setInputStream(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + } else { + throw new ResourceDoesNotExistException("No content provided for " + resource.getName()); + } + } + + @Override + public void fillOutputData(OutputData outputData) throws TransferFailedException { + outputData.setOutputStream(new ByteArrayOutputStream()); + } + + @Override + protected void openConnectionInternal() throws ConnectionException, AuthenticationException {} + + public void clearExpectedContent() { + expectedContent.clear(); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonA.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonA.java new file mode 100644 index 000000000000..f84fd328e44f --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonA.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Named; +import javax.inject.Singleton; + +/** + * Wagon for testing, for protocol a + * + */ +@Named("a") +@Singleton +public class WagonA extends WagonMock { + public String[] getSupportedProtocols() { + return new String[] {"a"}; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonB.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonB.java new file mode 100644 index 000000000000..5c8772a389e8 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonB.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Named; +import javax.inject.Singleton; + +/** + * Wagon for testing, for protocols b1 and b2 + * + */ +@Named("b") +@Singleton +public class WagonB extends WagonMock { + public String[] getSupportedProtocols() { + return new String[] {"b1", "b2"}; + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonC.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonC.java new file mode 100644 index 000000000000..2af580fbd935 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonC.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy; + +import javax.inject.Named; +import javax.inject.Singleton; + +/** + * Wagon for testing, for protocol c + * + */ +@Named("c") +@Singleton +public class WagonC extends WagonMock { + public String[] getSupportedProtocols() { + return new String[] {"c"}; + } +} diff --git a/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonMock.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonMock.java similarity index 81% rename from maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonMock.java rename to compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonMock.java index c5163609b015..c0abd118cc32 100644 --- a/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonMock.java +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/WagonMock.java @@ -1,5 +1,3 @@ -package org.apache.maven.repository.legacy; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -9,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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 @@ -18,17 +16,15 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.repository.legacy; import org.apache.maven.wagon.providers.file.FileWagon; /** * Mock of a Wagon for testing * - * @author Carlos Sanchez */ -public class WagonMock - extends FileWagon -{ +public class WagonMock extends FileWagon { /** * A field that can be configured in the Wagon @@ -37,14 +33,11 @@ public class WagonMock */ private String configurableField = null; - public void setConfigurableField( String configurableField ) - { + public void setConfigurableField(String configurableField) { this.configurableField = configurableField; } - public String getConfigurableField() - { + public String getConfigurableField() { return configurableField; } - } diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java new file mode 100644 index 000000000000..641d3034dfa8 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java @@ -0,0 +1,939 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver; + +import javax.inject.Inject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.CyclicDependencyException; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test the default artifact collector. + * + */ +@PlexusTest +@Deprecated +class DefaultArtifactCollectorTest { + @Inject + private LegacyArtifactCollector artifactCollector; + + @Inject + private ArtifactFactory artifactFactory; + + private ArtifactSpec projectArtifact; + + private Source source; + + private static final String GROUP_ID = "test"; + + @BeforeEach + void setUp() throws Exception { + source = new Source(); + + projectArtifact = createArtifactSpec("project", "1.0", null); + } + + @Test + @Disabled("works, but we don't fail on cycles presently") + void testCircularDependencyNotIncludingCurrentProject() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + b.addDependency("a", "1.0"); + assertThrows( + CyclicDependencyException.class, + () -> collect(a), + "Should have failed on cyclic dependency not involving project"); + } + + @Test + @Disabled("works, but we don't fail on cycles presently") + void testCircularDependencyIncludingCurrentProject() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + b.addDependency("project", "1.0"); + assertThrows( + CyclicDependencyException.class, + () -> collect(a), + "Should have failed on cyclic dependency not involving project"); + } + + @Test + void testResolveWithFilter() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + ArtifactSpec c = a.addDependency("c", "3.0"); + + b.addDependency("c", "2.0"); + ArtifactSpec d = b.addDependency("d", "4.0"); + + ArtifactResolutionResult res = collect(a); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, c.artifact, d.artifact}), + res.getArtifacts(), + "Check artifact list"); + + ArtifactFilter filter = new ExclusionSetFilter(new String[] {"b"}); + res = collect(a, filter); + assertEquals(createSet(new Object[] {a.artifact, c.artifact}), res.getArtifacts(), "Check artifact list"); + } + + @Test + void testResolveCorrectDependenciesWhenDifferentDependenciesOnNearest() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + ArtifactSpec c2 = b.addDependency("c", "2.0"); + c2.addDependency("d", "1.0"); + + ArtifactSpec e = createArtifactSpec("e", "1.0"); + ArtifactSpec c1 = e.addDependency("c", "1.0"); + ArtifactSpec f = c1.addDependency("f", "1.0"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, e.artifact})); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, e.artifact, c1.artifact, f.artifact}), + res.getArtifacts(), + "Check artifact list"); + assertEquals("1.0", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + @Disabled + void testResolveCorrectDependenciesWhenDifferentDependenciesOnNewest() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + // TODO use newest conflict resolver + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + ArtifactSpec c2 = b.addDependency("c", "2.0"); + ArtifactSpec d = c2.addDependency("d", "1.0"); + + ArtifactSpec e = createArtifactSpec("e", "1.0"); + ArtifactSpec c1 = e.addDependency("c", "1.0"); + c1.addDependency("f", "1.0"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, e.artifact})); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, e.artifact, c2.artifact, d.artifact}), + res.getArtifacts(), + "Check artifact list"); + assertEquals("2.0", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + @Disabled + void testResolveCorrectDependenciesWhenDifferentDependenciesOnNewestVersionReplaced() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + // TODO use newest conflict resolver + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b1 = a.addDependency("b", "1.0"); + ArtifactSpec c = a.addDependency("c", "1.0"); + ArtifactSpec d2 = b1.addDependency("d", "2.0"); + d2.addDependency("h", "1.0"); + ArtifactSpec d1 = c.addDependency("d", "1.0"); + ArtifactSpec b2 = c.addDependency("b", "2.0"); + ArtifactSpec e = b2.addDependency("e", "1.0"); + ArtifactSpec g = d1.addDependency("g", "1.0"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact})); + Object[] artifacts = new Object[] {a.artifact, c.artifact, d1.artifact, b2.artifact, e.artifact, g.artifact}; + assertEquals(createSet(artifacts), res.getArtifacts(), "Check artifact list"); + assertEquals("1.0", getArtifact("d", res.getArtifacts()).getVersion(), "Check version"); + assertEquals("2.0", getArtifact("b", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testResolveNearestNewestIsNearest() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + ArtifactSpec c = a.addDependency("c", "3.0"); + + b.addDependency("c", "2.0"); + + ArtifactResolutionResult res = collect(a); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, c.artifact}), + res.getArtifacts(), + "Check artifact list"); + assertEquals("3.0", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testResolveNearestOldestIsNearest() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + ArtifactSpec c = a.addDependency("c", "2.0"); + + b.addDependency("c", "3.0"); + + ArtifactResolutionResult res = collect(a); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, c.artifact}), + res.getArtifacts(), + "Check artifact list"); + assertEquals("2.0", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testResolveLocalNewestIsLocal() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + a.addDependency("b", "2.0"); + ArtifactSpec b = createArtifactSpec("b", "3.0"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + assertEquals("3.0", getArtifact("b", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testResolveLocalOldestIsLocal() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + a.addDependency("b", "3.0"); + ArtifactSpec b = createArtifactSpec("b", "2.0"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + assertEquals("2.0", getArtifact("b", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testResolveLocalWithNewerVersionButLesserScope() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("commons-logging", "1.0"); + a.addDependency("junit", "3.7"); + ArtifactSpec b = createArtifactSpec("junit", "3.8.1", Artifact.SCOPE_TEST); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + assertEquals("3.8.1", getArtifact("junit", res.getArtifacts()).getVersion(), "Check version"); + assertEquals( + Artifact.SCOPE_TEST, getArtifact("junit", res.getArtifacts()).getScope(), "Check artifactScope"); + } + + @Test + void testResolveLocalWithNewerVersionButLesserScopeResolvedFirst() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec b = createArtifactSpec("junit", "3.8.1", Artifact.SCOPE_TEST); + ArtifactSpec a = createArtifactSpec("commons-logging", "1.0"); + a.addDependency("junit", "3.7"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + assertEquals("3.8.1", getArtifact("junit", res.getArtifacts()).getVersion(), "Check version"); + assertEquals( + Artifact.SCOPE_TEST, getArtifact("junit", res.getArtifacts()).getScope(), "Check artifactScope"); + } + + @Test + void testResolveNearestWithRanges() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + ArtifactSpec c = a.addDependency("c", "2.0"); + + b.addDependency("c", "[1.0,3.0]"); + + ArtifactResolutionResult res = collect(a); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, c.artifact}), + res.getArtifacts(), + "Check artifact list"); + assertEquals("2.0", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + @SuppressWarnings("checkstyle:UnusedLocalVariable") + void testResolveRangeWithManagedVersion() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "[1.0,3.0]"); + + ArtifactSpec managedB = createArtifactSpec("b", "5.0"); + + ArtifactResolutionResult res = collect(a, managedB.artifact); + assertEquals( + createSet(new Object[] {a.artifact, managedB.artifact}), res.getArtifacts(), "Check artifact list"); + assertEquals("5.0", getArtifact("b", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testCompatibleRanges() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + a.addDependency("c", "[2.0,2.5]"); + b.addDependency("c", "[1.0,3.0]"); + ArtifactSpec c = createArtifactSpec("c", "2.5"); + + ArtifactResolutionResult res = collect(a); + + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, c.artifact}), + res.getArtifacts(), + "Check artifact list"); + assertEquals("2.5", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testIncompatibleRanges() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + a.addDependency("c", "[2.4,3.0]"); + + b.addDependency("c", "[1.0,2.0]"); + + ArtifactResolutionResult res = collect(a); + + assertTrue(res.hasVersionRangeViolations()); + } + + @Test + void testUnboundedRangeWhenVersionUnavailable() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = a.addDependency("b", "1.0"); + a.addDependency("c", "[2.0,]"); + b.addDependency("c", "[1.0,]"); + + ArtifactResolutionResult res = collect(a); + + assertTrue(res.hasVersionRangeViolations()); + } + + @Test + void testUnboundedRangeBelowLastRelease() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + createArtifactSpec("c", "1.5"); + ArtifactSpec c = createArtifactSpec("c", "2.0"); + createArtifactSpec("c", "1.1"); + a.addDependency("c", "[1.0,)"); + + ArtifactResolutionResult res = collect(a); + + assertEquals(createSet(new Object[] {a.artifact, c.artifact}), res.getArtifacts(), "Check artifact list"); + assertEquals("2.0", getArtifact("c", res.getArtifacts()).getVersion(), "Check version"); + } + + @Test + void testUnboundedRangeAboveLastRelease() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + createArtifactSpec("c", "2.0"); + a.addDependency("c", "[10.0,)"); + + ArtifactResolutionResult res = collect(a); + + assertTrue(res.hasVersionRangeViolations()); + } + + @Test + void testResolveManagedVersion() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + a.addDependency("b", "3.0", Artifact.SCOPE_RUNTIME); + + Artifact managedVersion = createArtifactSpec("b", "5.0").artifact; + Artifact modifiedB = createArtifactSpec("b", "5.0", Artifact.SCOPE_RUNTIME).artifact; + + ArtifactResolutionResult res = collect(a, managedVersion); + assertEquals(createSet(new Object[] {a.artifact, modifiedB}), res.getArtifacts(), "Check artifact list"); + } + + @Test + void testCollectChangesVersionOfOriginatingArtifactIfInDependencyManagementHasDifferentVersion() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + + Artifact artifact = projectArtifact.artifact; + Artifact managedVersion = createArtifactSpec(artifact.getArtifactId(), "2.0").artifact; + + ArtifactResolutionResult result = collect(a, managedVersion); + + assertEquals("1.0", artifact.getVersion(), "collect has modified version in originating artifact"); + + Artifact resolvedArtifact = result.getArtifacts().iterator().next(); + + assertEquals("1.0", resolvedArtifact.getVersion(), "Resolved version don't match original artifact version"); + } + + @Test + void testResolveCompileScopeOverTestScope() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec c = createArtifactSpec("c", "3.0", Artifact.SCOPE_TEST); + + a.addDependency("c", "2.0", Artifact.SCOPE_COMPILE); + + Artifact modifiedC = createArtifactSpec("c", "3.0", Artifact.SCOPE_COMPILE).artifact; + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, c.artifact})); + assertEquals(createSet(new Object[] {a.artifact, modifiedC}), res.getArtifacts(), "Check artifact list"); + Artifact artifact = getArtifact("c", res.getArtifacts()); + // local wins now, and irrelevant if not local as test/provided aren't transitive + // assertEquals( Artifact.SCOPE_COMPILE, artifact.getArtifactScope(), "Check artifactScope" ); + assertEquals(Artifact.SCOPE_TEST, artifact.getScope(), "Check artifactScope"); + } + + @Test + void testResolveRuntimeScopeOverTestScope() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec c = createArtifactSpec("c", "3.0", Artifact.SCOPE_TEST); + + a.addDependency("c", "2.0", Artifact.SCOPE_RUNTIME); + + Artifact modifiedC = createArtifactSpec("c", "3.0", Artifact.SCOPE_RUNTIME).artifact; + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, c.artifact})); + assertEquals(createSet(new Object[] {a.artifact, modifiedC}), res.getArtifacts(), "Check artifact list"); + Artifact artifact = getArtifact("c", res.getArtifacts()); + // local wins now, and irrelevant if not local as test/provided aren't transitive + // assertEquals( Artifact.SCOPE_RUNTIME, artifact.getArtifactScope(), "Check artifactScope" ); + assertEquals(Artifact.SCOPE_TEST, artifact.getScope(), "Check artifactScope"); + } + + @Test + void testResolveCompileScopeOverRuntimeScope() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec root = createArtifactSpec("root", "1.0"); + ArtifactSpec a = root.addDependency("a", "1.0"); + root.addDependency("c", "3.0", Artifact.SCOPE_RUNTIME); + + a.addDependency("c", "2.0", Artifact.SCOPE_COMPILE); + + Artifact modifiedC = createArtifactSpec("c", "3.0", Artifact.SCOPE_COMPILE).artifact; + + ArtifactResolutionResult res = collect(createSet(new Object[] {root.artifact})); + assertEquals( + createSet(new Object[] {a.artifact, root.artifact, modifiedC}), + res.getArtifacts(), + "Check artifact list"); + Artifact artifact = getArtifact("c", res.getArtifacts()); + assertEquals(Artifact.SCOPE_COMPILE, artifact.getScope(), "Check artifactScope"); + } + + @Test + void testResolveCompileScopeOverProvidedScope() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec c = createArtifactSpec("c", "3.0", Artifact.SCOPE_PROVIDED); + + a.addDependency("c", "2.0", Artifact.SCOPE_COMPILE); + + Artifact modifiedC = createArtifactSpec("c", "3.0", Artifact.SCOPE_COMPILE).artifact; + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, c.artifact})); + assertEquals(createSet(new Object[] {a.artifact, modifiedC}), res.getArtifacts(), "Check artifact list"); + Artifact artifact = getArtifact("c", res.getArtifacts()); + // local wins now, and irrelevant if not local as test/provided aren't transitive + // assertEquals( Artifact.SCOPE_COMPILE, artifact.getArtifactScope(), "Check artifactScope" ); + assertEquals(Artifact.SCOPE_PROVIDED, artifact.getScope(), "Check artifactScope"); + } + + @Test + void testResolveRuntimeScopeOverProvidedScope() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec c = createArtifactSpec("c", "3.0", Artifact.SCOPE_PROVIDED); + + a.addDependency("c", "2.0", Artifact.SCOPE_RUNTIME); + + Artifact modifiedC = createArtifactSpec("c", "3.0", Artifact.SCOPE_RUNTIME).artifact; + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, c.artifact})); + assertEquals(createSet(new Object[] {a.artifact, modifiedC}), res.getArtifacts(), "Check artifact list"); + Artifact artifact = getArtifact("c", res.getArtifacts()); + // local wins now, and irrelevant if not local as test/provided aren't transitive + // assertEquals( Artifact.SCOPE_RUNTIME, artifact.getArtifactScope(), "Check artifactScope" ); + assertEquals(Artifact.SCOPE_PROVIDED, artifact.getScope(), "Check artifactScope"); + } + + @Test + void testProvidedScopeNotTransitive() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0", Artifact.SCOPE_PROVIDED); + ArtifactSpec b = createArtifactSpec("b", "1.0"); + b.addDependency("c", "3.0", Artifact.SCOPE_PROVIDED); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + } + + @Test + void testOptionalNotTransitive() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = createArtifactSpec("b", "1.0"); + b.addDependency("c", "3.0", true); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + } + + @Test + void testOptionalIncludedAtRoot() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + + ArtifactSpec b = createArtifactSpec("b", "1.0", true); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + } + + @Test + void testScopeUpdate() throws InvalidVersionSpecificationException, ArtifactResolutionException { + /* farthest = compile */ + checkScopeUpdate(Artifact.SCOPE_COMPILE, Artifact.SCOPE_COMPILE, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST, Artifact.SCOPE_COMPILE); + + /* farthest = provided */ + checkScopeUpdate(Artifact.SCOPE_PROVIDED, Artifact.SCOPE_COMPILE, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_PROVIDED, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_PROVIDED); + checkScopeUpdate(Artifact.SCOPE_PROVIDED, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_RUNTIME); + checkScopeUpdate(Artifact.SCOPE_PROVIDED, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_SYSTEM); + checkScopeUpdate(Artifact.SCOPE_PROVIDED, Artifact.SCOPE_TEST, Artifact.SCOPE_TEST); + + /* farthest = runtime */ + checkScopeUpdate(Artifact.SCOPE_RUNTIME, Artifact.SCOPE_COMPILE, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_RUNTIME, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_RUNTIME); + checkScopeUpdate(Artifact.SCOPE_RUNTIME, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_RUNTIME); + checkScopeUpdate(Artifact.SCOPE_RUNTIME, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_SYSTEM); + checkScopeUpdate(Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST, Artifact.SCOPE_RUNTIME); + + /* farthest = system */ + checkScopeUpdate(Artifact.SCOPE_SYSTEM, Artifact.SCOPE_COMPILE, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_PROVIDED); + checkScopeUpdate(Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_RUNTIME); + checkScopeUpdate(Artifact.SCOPE_SYSTEM, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_SYSTEM); + checkScopeUpdate(Artifact.SCOPE_SYSTEM, Artifact.SCOPE_TEST, Artifact.SCOPE_TEST); + + /* farthest = test */ + checkScopeUpdate(Artifact.SCOPE_TEST, Artifact.SCOPE_COMPILE, Artifact.SCOPE_COMPILE); + checkScopeUpdate(Artifact.SCOPE_TEST, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_PROVIDED); + checkScopeUpdate(Artifact.SCOPE_TEST, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_RUNTIME); + checkScopeUpdate(Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_SYSTEM); + checkScopeUpdate(Artifact.SCOPE_TEST, Artifact.SCOPE_TEST, Artifact.SCOPE_TEST); + } + + private void checkScopeUpdate(String farthestScope, String nearestScope, String expectedScope) + throws ArtifactResolutionException, InvalidVersionSpecificationException { + checkScopeUpdateDirect(farthestScope, nearestScope, expectedScope); + checkScopeUpdateTransitively(farthestScope, nearestScope, expectedScope); + } + + private void checkScopeUpdateTransitively(String farthestScope, String nearestScope, String expectedScope) + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = createArtifactSpec("b", "1.0", nearestScope); + ArtifactSpec c = createArtifactSpec("c", "1.0"); + a.addDependency(c); + ArtifactSpec dNearest = createArtifactSpec("d", "2.0"); + b.addDependency(dNearest); + ArtifactSpec dFarthest = createArtifactSpec("d", "3.0", farthestScope); + c.addDependency(dFarthest); + + /* system and provided dependencies are not transitive */ + if (!Artifact.SCOPE_SYSTEM.equals(nearestScope) && !Artifact.SCOPE_PROVIDED.equals(nearestScope)) { + checkScopeUpdate(a, b, expectedScope, "2.0"); + } + } + + private void checkScopeUpdateDirect(String farthestScope, String nearestScope, String expectedScope) + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = createArtifactSpec("b", "1.0"); + ArtifactSpec c = createArtifactSpec("c", "1.0"); + a.addDependency(c); + ArtifactSpec dNearest = createArtifactSpec("d", "2.0", nearestScope); + b.addDependency(dNearest); + ArtifactSpec dFarthest = createArtifactSpec("d", "3.0", farthestScope); + c.addDependency(dFarthest); + + checkScopeUpdate(a, b, expectedScope, "2.0"); + } + + private void checkScopeUpdate(ArtifactSpec a, ArtifactSpec b, String expectedScope, String expectedVersion) + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ScopeArtifactFilter filter; + if (Artifact.SCOPE_PROVIDED.equals(expectedScope)) { + filter = new ScopeArtifactFilter(Artifact.SCOPE_COMPILE); + } else if (Artifact.SCOPE_SYSTEM.equals(expectedScope)) { + filter = new ScopeArtifactFilter(Artifact.SCOPE_COMPILE); + } else { + filter = new ScopeArtifactFilter(expectedScope); + } + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact}), filter); + Artifact artifact = getArtifact("d", res.getArtifacts()); + assertNotNull(artifact, "MNG-1895 Dependency was not added to resolution"); + assertEquals(expectedScope, artifact.getScope(), "Check artifactScope"); + assertEquals(expectedVersion, artifact.getVersion(), "Check version"); + + ArtifactSpec d = createArtifactSpec("d", "1.0"); + res = collect(createSet(new Object[] {a.artifact, b.artifact, d.artifact}), filter); + artifact = getArtifact("d", res.getArtifacts()); + assertNotNull(artifact, "MNG-1895 Dependency was not added to resolution"); + assertEquals(d.artifact.getScope(), artifact.getScope(), "Check artifactScope"); + assertEquals("1.0", artifact.getVersion(), "Check version"); + } + + @Test + @Disabled + void testOptionalNotTransitiveButVersionIsInfluential() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + ArtifactSpec b = createArtifactSpec("b", "1.0"); + b.addDependency("c", "3.0", true); + ArtifactSpec d = a.addDependency("d", "1.0"); + ArtifactSpec e = d.addDependency("e", "1.0"); + e.addDependency("c", "2.0"); + + ArtifactSpec c = createArtifactSpec("c", "3.0"); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals( + createSet(new Object[] {a.artifact, b.artifact, c.artifact, d.artifact, e.artifact}), + res.getArtifacts(), + "Check artifact list"); + Artifact artifact = getArtifact("c", res.getArtifacts()); + assertEquals("3.0", artifact.getVersion(), "Check version"); + } + + @Test + void testTestScopeNotTransitive() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0", Artifact.SCOPE_TEST); + ArtifactSpec b = createArtifactSpec("b", "1.0"); + b.addDependency("c", "3.0", Artifact.SCOPE_TEST); + + ArtifactResolutionResult res = collect(createSet(new Object[] {a.artifact, b.artifact})); + assertEquals(createSet(new Object[] {a.artifact, b.artifact}), res.getArtifacts(), "Check artifact list"); + } + + @Test + void testSnapshotNotIncluded() throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + a.addDependency("b", "[1.0,)"); + createArtifactSpec("b", "1.0-SNAPSHOT"); + + ArtifactResolutionResult res = collect(a); + + assertTrue(res.hasVersionRangeViolations()); + + /* + * try { ArtifactResolutionResult res = collect( a ); fail( "Expected b not to resolve: " + res ); } catch ( + * OverConstrainedVersionException e ) { assertTrue( e.getMessage().indexOf( "[1.0-SNAPSHOT]" ) < + * e.getMessage().indexOf( "[1.0,)" ) ); } + */ + } + + @Test + @Disabled("that one does not work") + @SuppressWarnings("checkstyle:UnusedLocalVariable") + void testOverConstrainedVersionException() + throws ArtifactResolutionException, InvalidVersionSpecificationException { + ArtifactSpec a = createArtifactSpec("a", "1.0"); + a.addDependency("b", "[1.0, 2.0)"); + a.addDependency("c", "[3.3.0,4.0.0)"); + + ArtifactSpec b = createArtifactSpec("b", "1.0.0"); + b.addDependency("c", "3.3.0-v3346"); + + ArtifactSpec c = createArtifactSpec("c", "3.2.1-v3235e"); + + OverConstrainedVersionException e = assertThrows( + OverConstrainedVersionException.class, () -> collect(createSet(new Object[] {a.artifact}))); + assertTrue(e.getMessage().contains("[3.2.1-v3235e, 3.3.0-v3346]"), "Versions unordered"); + assertTrue(e.getMessage().contains("Path to dependency:"), "DependencyTrail unresolved"); + } + + private Artifact getArtifact(String id, Set artifacts) { + for (Object artifact : artifacts) { + Artifact a = (Artifact) artifact; + if (a.getArtifactId().equals(id) && a.getGroupId().equals(GROUP_ID)) { + return a; + } + } + return null; + } + + private ArtifactResolutionResult collect(Set artifacts) throws ArtifactResolutionException { + return collect(artifacts, null); + } + + private ArtifactResolutionResult collect(Set artifacts, ArtifactFilter filter) + throws ArtifactResolutionException { + return artifactCollector.collect( + artifacts, projectArtifact.artifact, null, null, null, source, filter, Collections.emptyList(), null); + } + + private ArtifactResolutionResult collect(ArtifactSpec a) throws ArtifactResolutionException { + return artifactCollector.collect( + Collections.singleton(a.artifact), + projectArtifact.artifact, + null, + null, + null, + source, + null, + Collections.emptyList(), + null); + } + + private ArtifactResolutionResult collect(ArtifactSpec a, ArtifactFilter filter) throws ArtifactResolutionException { + return artifactCollector.collect( + Collections.singleton(a.artifact), + projectArtifact.artifact, + null, + null, + null, + source, + filter, + Collections.emptyList(), + null); + } + + private ArtifactResolutionResult collect(ArtifactSpec a, Artifact managedVersion) + throws ArtifactResolutionException { + Map managedVersions = + Collections.singletonMap(managedVersion.getDependencyConflictId(), managedVersion); + return artifactCollector.collect( + Collections.singleton(a.artifact), + projectArtifact.artifact, + managedVersions, + null, + null, + source, + null, + Collections.emptyList(), + null); + } + + private ArtifactSpec createArtifactSpec(String id, String version) throws InvalidVersionSpecificationException { + return createArtifactSpec(id, version, Artifact.SCOPE_COMPILE); + } + + private ArtifactSpec createArtifactSpec(String id, String version, boolean optional) + throws InvalidVersionSpecificationException { + return createArtifactSpec(id, version, Artifact.SCOPE_COMPILE, null, optional); + } + + private ArtifactSpec createArtifactSpec(String id, String version, String scope) + throws InvalidVersionSpecificationException { + return createArtifactSpec(id, version, scope, null, false); + } + + private ArtifactSpec createArtifactSpec( + String id, String version, String scope, String inheritedScope, boolean optional) + throws InvalidVersionSpecificationException { + VersionRange versionRange = VersionRange.createFromVersionSpec(version); + Artifact artifact = artifactFactory.createDependencyArtifact( + GROUP_ID, id, versionRange, "jar", null, scope, inheritedScope, optional); + ArtifactSpec spec = null; + if (artifact != null) { + spec = new ArtifactSpec(); + spec.artifact = artifact; + source.addArtifact(spec); + } + return spec; + } + + @SuppressWarnings("unchecked") + private static Set createSet(Object[] x) { + return new LinkedHashSet(Arrays.asList(x)); + } + + private class ArtifactSpec { + private Artifact artifact; + + private Set dependencies = new HashSet<>(); + + public ArtifactSpec addDependency(String id, String version) throws InvalidVersionSpecificationException { + return addDependency(id, version, Artifact.SCOPE_COMPILE); + } + + public ArtifactSpec addDependency(String id, String version, String scope) + throws InvalidVersionSpecificationException { + return addDependency(id, version, scope, false); + } + + private ArtifactSpec addDependency(ArtifactSpec dep) throws InvalidVersionSpecificationException { + if (dep != null) { + dependencies.add(dep.artifact); + } + return dep; + } + + private ArtifactSpec addDependency(String id, String version, String scope, boolean optional) + throws InvalidVersionSpecificationException { + ArtifactSpec dep = createArtifactSpec(id, version, scope, artifact.getScope(), optional); + return addDependency(dep); + } + + public ArtifactSpec addDependency(String id, String version, boolean optional) + throws InvalidVersionSpecificationException { + return addDependency(id, version, Artifact.SCOPE_COMPILE, optional); + } + } + + private class Source implements ArtifactMetadataSource { + private Map artifacts = new HashMap<>(); + + private Map> versions = new HashMap<>(); + + @Override + public ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + String key = getKey(artifact); + + ArtifactSpec a = artifacts.get(key); + try { + return new ResolutionGroup( + artifact, + createArtifacts( + artifactFactory, a.dependencies, artifact.getScope(), artifact.getDependencyFilter()), + Collections.emptyList()); + } catch (InvalidVersionSpecificationException e) { + throw new ArtifactMetadataRetrievalException("Invalid version creating artifacts", e, artifact); + } + } + + private String getKey(Artifact artifact) { + return artifact.getDependencyConflictId(); + } + + private Set createArtifacts( + ArtifactFactory artifactFactory, + Set dependencies, + String inheritedScope, + ArtifactFilter dependencyFilter) + throws InvalidVersionSpecificationException { + Set projectArtifacts = new HashSet<>(); + + for (Artifact d : dependencies) { + VersionRange versionRange; + if (d.getVersionRange() != null) { + versionRange = d.getVersionRange(); + } else { + versionRange = VersionRange.createFromVersionSpec(d.getVersion()); + } + Artifact artifact; + if (d.getScope().equals(Artifact.SCOPE_TEST) || d.getScope().equals(Artifact.SCOPE_PROVIDED)) { + /* don't call createDependencyArtifact as it'll ignore test and provided scopes */ + artifact = artifactFactory.createArtifact( + d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getScope(), d.getType()); + } else { + artifact = artifactFactory.createDependencyArtifact( + d.getGroupId(), + d.getArtifactId(), + versionRange, + d.getType(), + d.getClassifier(), + d.getScope(), + inheritedScope, + d.isOptional()); + } + + if (artifact != null && (dependencyFilter == null || dependencyFilter.include(artifact))) { + artifact.setDependencyFilter(dependencyFilter); + + projectArtifacts.add(artifact); + } + } + + return projectArtifacts; + } + + public void addArtifact(ArtifactSpec spec) { + artifacts.put(getKey(spec.artifact), spec); + + String key = spec.artifact.getDependencyConflictId(); + List artifactVersions = versions.computeIfAbsent(key, k -> new ArrayList<>()); + if (spec.artifact.getVersion() != null) { + artifactVersions.add(new DefaultArtifactVersion(spec.artifact.getVersion())); + } + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + return retrieveAvailableVersions(artifact); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws ArtifactMetadataRetrievalException { + return retrieveAvailableVersions(artifact); + } + + private List retrieveAvailableVersions(Artifact artifact) { + List artifactVersions = versions.get(artifact.getDependencyConflictId()); + if (artifactVersions == null) { + artifactVersions = Collections.emptyList(); + } + return artifactVersions; + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException { + return retrieve(request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } + + @Override + public List retrieveAvailableVersions(MetadataResolutionRequest request) + throws ArtifactMetadataRetrievalException { + return retrieveAvailableVersions( + request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/AbstractConflictResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/AbstractConflictResolverTest.java new file mode 100644 index 000000000000..2bbfc00915fd --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/AbstractConflictResolverTest.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import javax.inject.Inject; + +import java.util.Collections; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.BeforeEach; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Provides a basis for testing conflict resolvers. + * + */ +@PlexusTest +@Deprecated +public abstract class AbstractConflictResolverTest { + // constants -------------------------------------------------------------- + + private static final String GROUP_ID = "test"; + + // fields ----------------------------------------------------------------- + + protected Artifact a1; + + protected Artifact a2; + + protected Artifact b1; + + private final String roleHint; + + @Inject + private ArtifactFactory artifactFactory; + + private ConflictResolver conflictResolver; + + @Inject + protected PlexusContainer container; + + // constructors ----------------------------------------------------------- + + public AbstractConflictResolverTest(String roleHint) throws Exception { + this.roleHint = roleHint; + } + + // TestCase methods ------------------------------------------------------- + + /* + * @see junit.framework.TestCase#setUp() + */ + @BeforeEach + public void setUp() throws Exception { + conflictResolver = (ConflictResolver) container.lookup(ConflictResolver.ROLE, roleHint); + + a1 = createArtifact("a", "1.0"); + a2 = createArtifact("a", "2.0"); + b1 = createArtifact("b", "1.0"); + } + + // protected methods ------------------------------------------------------ + + protected ConflictResolver getConflictResolver() { + return conflictResolver; + } + + protected void assertResolveConflict( + ResolutionNode expectedNode, ResolutionNode actualNode1, ResolutionNode actualNode2) { + ResolutionNode resolvedNode = getConflictResolver().resolveConflict(actualNode1, actualNode2); + + assertNotNull(resolvedNode, "Expected resolvable"); + assertEquals(expectedNode, resolvedNode, "Resolution node"); + } + + protected Artifact createArtifact(String id, String version) throws InvalidVersionSpecificationException { + return createArtifact(id, version, Artifact.SCOPE_COMPILE); + } + + protected Artifact createArtifact(String id, String version, String scope) + throws InvalidVersionSpecificationException { + return createArtifact(id, version, scope, null, false); + } + + protected Artifact createArtifact(String id, String version, String scope, String inheritedScope, boolean optional) + throws InvalidVersionSpecificationException { + VersionRange versionRange = VersionRange.createFromVersionSpec(version); + + return artifactFactory.createDependencyArtifact( + GROUP_ID, id, versionRange, "jar", null, scope, inheritedScope, optional); + } + + protected ResolutionNode createResolutionNode(Artifact artifact) { + return new ResolutionNode(artifact, Collections.emptyList()); + } + + protected ResolutionNode createResolutionNode(Artifact artifact, ResolutionNode parent) { + return new ResolutionNode(artifact, Collections.emptyList(), parent); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/FarthestConflictResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/FarthestConflictResolverTest.java new file mode 100644 index 000000000000..25aa5124f9da --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/FarthestConflictResolverTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.junit.jupiter.api.Test; + +/** + * Tests FarthestConflictResolver. + * + * @see FarthestConflictResolver + */ +@Deprecated +class FarthestConflictResolverTest extends AbstractConflictResolverTest { + // constructors ----------------------------------------------------------- + + FarthestConflictResolverTest() throws Exception { + super("farthest"); + } + + // tests ------------------------------------------------------------------ + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * a:1.0
    +     * b:1.0 -> a:2.0
    +     * 
    + */ + @Test + void testDepth() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + + assertResolveConflict(a2n, a1n, a2n); + } + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * b:1.0 -> a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testDepthReversed() { + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a2n, a2n, a1n); + } + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * a:1.0
    +     * a:2.0
    +     * 
    + */ + @Test + void testEqual() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode a2n = createResolutionNode(a2); + + assertResolveConflict(a1n, a1n, a2n); + } + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testEqualReversed() { + ResolutionNode a2n = createResolutionNode(a2); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a2n, a2n, a1n); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/NearestConflictResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/NearestConflictResolverTest.java new file mode 100644 index 000000000000..605d2aca0858 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/NearestConflictResolverTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.junit.jupiter.api.Test; + +/** + * Tests NearestConflictResolver. + * + * @see NearestConflictResolver + */ +@Deprecated +class NearestConflictResolverTest extends AbstractConflictResolverTest { + // constructors ----------------------------------------------------------- + + NearestConflictResolverTest() throws Exception { + super("nearest"); + } + + // tests ------------------------------------------------------------------ + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * a:1.0
    +     * b:1.0 -> a:2.0
    +     * 
    + */ + @Test + void testDepth() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + + assertResolveConflict(a1n, a1n, a2n); + } + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * b:1.0 -> a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testDepthReversed() { + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a1n, a2n, a1n); + } + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * a:1.0
    +     * a:2.0
    +     * 
    + */ + @Test + void testEqual() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode a2n = createResolutionNode(a2); + + assertResolveConflict(a1n, a1n, a2n); + } + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testEqualReversed() { + ResolutionNode a2n = createResolutionNode(a2); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a2n, a2n, a1n); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/NewestConflictResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/NewestConflictResolverTest.java new file mode 100644 index 000000000000..c8b46637f7ac --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/NewestConflictResolverTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.junit.jupiter.api.Test; + +/** + * Tests NewestConflictResolver. + * + * @see NewestConflictResolver + */ +@Deprecated +class NewestConflictResolverTest extends AbstractConflictResolverTest { + // constructors ----------------------------------------------------------- + + NewestConflictResolverTest() throws Exception { + super("newest"); + } + + // tests ------------------------------------------------------------------ + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * a:1.0
    +     * b:1.0 -> a:2.0
    +     * 
    + */ + @Test + void testDepth() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + + assertResolveConflict(a2n, a1n, a2n); + } + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * b:1.0 -> a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testDepthReversed() { + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a2n, a2n, a1n); + } + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * a:1.0
    +     * a:2.0
    +     * 
    + */ + @Test + void testEqual() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode a2n = createResolutionNode(a2); + + assertResolveConflict(a2n, a1n, a2n); + } + + /** + * Tests that a:2.0 wins in the scenario: + *
    +     * a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testEqualReversed() { + ResolutionNode a2n = createResolutionNode(a2); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a2n, a2n, a1n); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/OldestConflictResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/OldestConflictResolverTest.java new file mode 100644 index 000000000000..153f00c8ceed --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/conflict/OldestConflictResolverTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.legacy.resolver.conflict; + +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.junit.jupiter.api.Test; + +/** + * Tests OldestConflictResolver. + * + * @see OldestConflictResolver + */ +@Deprecated +class OldestConflictResolverTest extends AbstractConflictResolverTest { + // constructors ----------------------------------------------------------- + + OldestConflictResolverTest() throws Exception { + super("oldest"); + } + + // tests ------------------------------------------------------------------ + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * a:1.0
    +     * b:1.0 -> a:2.0
    +     * 
    + */ + @Test + void testDepth() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + + assertResolveConflict(a1n, a1n, a2n); + } + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * b:1.0 -> a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testDepthReversed() { + ResolutionNode b1n = createResolutionNode(b1); + ResolutionNode a2n = createResolutionNode(a2, b1n); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a1n, a2n, a1n); + } + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * a:1.0
    +     * a:2.0
    +     * 
    + */ + @Test + void testEqual() { + ResolutionNode a1n = createResolutionNode(a1); + ResolutionNode a2n = createResolutionNode(a2); + + assertResolveConflict(a1n, a1n, a2n); + } + + /** + * Tests that a:1.0 wins in the scenario: + *
    +     * a:2.0
    +     * a:1.0
    +     * 
    + */ + @Test + void testEqualReversed() { + ResolutionNode a2n = createResolutionNode(a2); + ResolutionNode a1n = createResolutionNode(a1); + + assertResolveConflict(a1n, a2n, a1n); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultClasspathTransformationTestType.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultClasspathTransformationTestType.java new file mode 100644 index 000000000000..495db2d21528 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultClasspathTransformationTestType.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import javax.inject.Inject; + +import org.apache.maven.artifact.ArtifactScopeEnum; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * + * + */ +@PlexusTest +@Deprecated +class DefaultClasspathTransformationTestType { + @Inject + ClasspathTransformation transform; + + MetadataGraph graph; + + MetadataGraphVertex v1; + MetadataGraphVertex v2; + MetadataGraphVertex v3; + MetadataGraphVertex v4; + + // ------------------------------------------------------------------------------------------ + @BeforeEach + void setUp() throws Exception { + graph = new MetadataGraph(4, 3); + /* + * v2 + * v1< + * v3-v4 + * + */ + v1 = graph.addVertex(new ArtifactMetadata("g", "a1", "1.0")); + graph.setEntry(v1); + v2 = graph.addVertex(new ArtifactMetadata("g", "a2", "1.0")); + v3 = graph.addVertex(new ArtifactMetadata("g", "a3", "1.0")); + v4 = graph.addVertex(new ArtifactMetadata("g", "a4", "1.0")); + + // v1-->v2 + graph.addEdge(v1, v2, new MetadataGraphEdge("1.1", true, null, null, 2, 1)); + graph.addEdge(v1, v2, new MetadataGraphEdge("1.2", true, null, null, 2, 2)); + + // v1-->v3 + graph.addEdge(v1, v3, new MetadataGraphEdge("1.1", true, null, null, 2, 1)); + graph.addEdge(v1, v3, new MetadataGraphEdge("1.2", true, null, null, 4, 2)); + + // v3-->v4 + graph.addEdge(v3, v4, new MetadataGraphEdge("1.1", true, ArtifactScopeEnum.runtime, null, 2, 2)); + graph.addEdge(v3, v4, new MetadataGraphEdge("1.2", true, ArtifactScopeEnum.test, null, 2, 2)); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testCompileClasspathTransform() throws Exception { + ClasspathContainer res; + + res = transform.transform(graph, ArtifactScopeEnum.compile, false); + + assertNotNull(res, "null classpath container after compile transform"); + assertNotNull(res.getClasspath(), "null classpath after compile transform"); + assertEquals(3, res.getClasspath().size(), "compile classpath should have 3 entries"); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testRuntimeClasspathTransform() throws Exception { + ClasspathContainer res; + + res = transform.transform(graph, ArtifactScopeEnum.runtime, false); + + assertNotNull(res, "null classpath container after runtime transform"); + assertNotNull(res.getClasspath(), "null classpath after runtime transform"); + assertEquals(4, res.getClasspath().size(), "runtime classpath should have 4 entries"); + + ArtifactMetadata md = res.getClasspath().get(3); + assertEquals("1.1", md.getVersion(), "runtime artifact version should be 1.1"); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testTestClasspathTransform() throws Exception { + ClasspathContainer res; + + res = transform.transform(graph, ArtifactScopeEnum.test, false); + + assertNotNull(res, "null classpath container after test transform"); + assertNotNull(res.getClasspath(), "null classpath after test transform"); + assertEquals(4, res.getClasspath().size(), "test classpath should have 4 entries"); + + ArtifactMetadata md = res.getClasspath().get(3); + assertEquals("1.2", md.getVersion(), "test artifact version should be 1.2"); + } + // ------------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------------ +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolutionPolicyTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolutionPolicyTest.java new file mode 100644 index 000000000000..15da1b3315ab --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolutionPolicyTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + * + */ +@Deprecated +class DefaultGraphConflictResolutionPolicyTest { + GraphConflictResolutionPolicy policy; + MetadataGraphEdge e1; + MetadataGraphEdge e2; + MetadataGraphEdge e3; + + // ------------------------------------------------------------------------------------------ + @BeforeEach + void setUp() throws Exception { + policy = new DefaultGraphConflictResolutionPolicy(); + e1 = new MetadataGraphEdge("1.1", true, null, null, 2, 1); + e2 = new MetadataGraphEdge("1.2", true, null, null, 3, 2); + e3 = new MetadataGraphEdge("1.2", true, null, null, 2, 3); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testDefaultPolicy() throws Exception { + MetadataGraphEdge res; + + res = policy.apply(e1, e2); + assertEquals("1.1", res.getVersion(), "Wrong depth edge selected"); + + res = policy.apply(e1, e3); + assertEquals("1.2", res.getVersion(), "Wrong version edge selected"); + } + // ------------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------------ +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolverTest.java new file mode 100644 index 000000000000..94625d1fc0e9 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolverTest.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import javax.inject.Inject; + +import org.apache.maven.artifact.ArtifactScopeEnum; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * + * + */ +@PlexusTest +@Deprecated +class DefaultGraphConflictResolverTest { + @Inject + GraphConflictResolver resolver; + + MetadataGraph graph; + + MetadataGraphVertex v1; + MetadataGraphVertex v2; + MetadataGraphVertex v3; + MetadataGraphVertex v4; + + // ------------------------------------------------------------------------------------------ + @BeforeEach + void setUp() throws Exception { + /* + * v2 + * v1< + * v3-v4 + * + */ + graph = new MetadataGraph(4, 3); + v1 = graph.addVertex(new ArtifactMetadata("g", "a1", "1.0")); + graph.setEntry(v1); + v2 = graph.addVertex(new ArtifactMetadata("g", "a2", "1.0")); + v3 = graph.addVertex(new ArtifactMetadata("g", "a3", "1.0")); + v4 = graph.addVertex(new ArtifactMetadata("g", "a4", "1.0")); + + // v1-->v2 + graph.addEdge(v1, v2, new MetadataGraphEdge("1.1", true, null, null, 2, 1)); + graph.addEdge(v1, v2, new MetadataGraphEdge("1.2", true, null, null, 2, 2)); + + // v1-->v3 + graph.addEdge(v1, v3, new MetadataGraphEdge("1.1", true, null, null, 2, 1)); + graph.addEdge(v1, v3, new MetadataGraphEdge("1.2", true, null, null, 4, 2)); + + // v3-->v4 + graph.addEdge(v3, v4, new MetadataGraphEdge("1.1", true, ArtifactScopeEnum.runtime, null, 2, 1)); + graph.addEdge(v3, v4, new MetadataGraphEdge("1.2", true, ArtifactScopeEnum.provided, null, 2, 2)); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testCompileResolution() throws Exception { + MetadataGraph res; + + res = resolver.resolveConflicts(graph, ArtifactScopeEnum.compile); + + assertNotNull(res, "null graph after resolver"); + assertNotNull(res.getVertices(), "no vertices in the resulting graph after resolver"); + + assertNotNull(res.getExcidentEdges(v1), "no edges in the resulting graph after resolver"); + + assertEquals(4, res.getVertices().size(), "wrong # of vertices in the resulting graph after resolver"); + assertEquals( + 2, + res.getExcidentEdges(v1).size(), + "wrong # of excident edges in the resulting graph entry after resolver"); + + assertEquals( + 1, + res.getIncidentEdges(v2).size(), + "wrong # of v2 incident edges in the resulting graph after resolver"); + assertEquals( + "1.2", + res.getIncidentEdges(v2).get(0).getVersion(), + "wrong edge v1-v2 in the resulting graph after resolver"); + + assertEquals( + 1, res.getIncidentEdges(v3).size(), "wrong # of edges v1-v3 in the resulting graph after resolver"); + assertEquals( + "1.1", + res.getIncidentEdges(v3).get(0).getVersion(), + "wrong edge v1-v3 in the resulting graph after resolver"); + + assertEquals( + 1, res.getIncidentEdges(v4).size(), "wrong # of edges v3-v4 in the resulting graph after resolver"); + assertEquals( + "1.2", + res.getIncidentEdges(v4).get(0).getVersion(), + "wrong edge v3-v4 in the resulting graph after resolver"); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testRuntimeResolution() throws Exception { + MetadataGraph res; + + res = resolver.resolveConflicts(graph, ArtifactScopeEnum.runtime); + + assertNotNull(res, "null graph after resolver"); + assertNotNull(res.getVertices(), "no vertices in the resulting graph after resolver"); + assertNotNull(res.getExcidentEdges(v1), "no edges in the resulting graph after resolver"); + + assertEquals(4, res.getVertices().size(), "wrong # of vertices in the resulting graph after resolver"); + assertEquals( + 2, + res.getExcidentEdges(v1).size(), + "wrong # of excident edges in the resulting graph entry after resolver"); + + assertEquals( + 1, + res.getIncidentEdges(v2).size(), + "wrong # of v2 incident edges in the resulting graph after resolver"); + assertEquals( + "1.2", + res.getIncidentEdges(v2).get(0).getVersion(), + "wrong edge v1-v2 in the resulting graph after resolver"); + + assertEquals( + 1, res.getIncidentEdges(v3).size(), "wrong # of edges v1-v3 in the resulting graph after resolver"); + assertEquals( + "1.1", + res.getIncidentEdges(v3).get(0).getVersion(), + "wrong edge v1-v3 in the resulting graph after resolver"); + + assertEquals( + 1, res.getIncidentEdges(v4).size(), "wrong # of edges v3-v4 in the resulting graph after resolver"); + assertEquals( + "1.1", + res.getIncidentEdges(v4).get(0).getVersion(), + "wrong edge v3-v4 in the resulting graph after resolver"); + } + + // ------------------------------------------------------------------------------------------ + @Test + void testTestResolution() throws Exception { + MetadataGraph res; + + res = resolver.resolveConflicts(graph, ArtifactScopeEnum.test); + + assertNotNull(res, "null graph after resolver"); + assertNotNull(res.getVertices(), "no vertices in the resulting graph after resolver"); + assertNotNull(res.getExcidentEdges(v1), "no edges in the resulting graph after resolver"); + + assertEquals(4, res.getVertices().size(), "wrong # of vertices in the resulting graph after resolver"); + assertEquals( + 2, + res.getExcidentEdges(v1).size(), + "wrong # of excident edges in the resulting graph entry after resolver"); + + assertEquals( + 1, + res.getIncidentEdges(v2).size(), + "wrong # of v2 incident edges in the resulting graph after resolver"); + assertEquals( + "1.2", + res.getIncidentEdges(v2).get(0).getVersion(), + "wrong edge v1-v2 in the resulting graph after resolver"); + + assertEquals( + 1, res.getIncidentEdges(v3).size(), "wrong # of edges v1-v3 in the resulting graph after resolver"); + assertEquals( + "1.1", + res.getIncidentEdges(v3).get(0).getVersion(), + "wrong edge v1-v3 in the resulting graph after resolver"); + + assertEquals( + 1, res.getIncidentEdges(v4).size(), "wrong # of edges v3-v4 in the resulting graph after resolver"); + assertEquals( + "1.2", + res.getIncidentEdges(v4).get(0).getVersion(), + "wrong edge v3-v4 in the resulting graph after resolver"); + } + // ------------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------------ +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/TestMetadataSource.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/TestMetadataSource.java new file mode 100644 index 000000000000..dd11b2a9554f --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/metadata/TestMetadataSource.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.repository.metadata; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException; +import org.apache.maven.repository.legacy.metadata.ArtifactMetadataSource; +import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; +import org.apache.maven.repository.legacy.metadata.ResolutionGroup; + +@Named +@Singleton +@Deprecated +public class TestMetadataSource implements ArtifactMetadataSource { + @Inject + private ArtifactFactory factory; + + @Override + public ResolutionGroup retrieve( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + Set dependencies = new HashSet<>(); + + if ("g".equals(artifact.getArtifactId())) { + Artifact a = null; + try { + a = factory.createBuildArtifact("org.apache.maven", "h", "1.0", "jar"); + dependencies.add(a); + } catch (Exception e) { + throw new ArtifactMetadataRetrievalException("Error retrieving metadata", e, a); + } + } + + if ("i".equals(artifact.getArtifactId())) { + Artifact a = null; + try { + a = factory.createBuildArtifact("org.apache.maven", "j", "1.0-SNAPSHOT", "jar"); + dependencies.add(a); + } catch (Exception e) { + throw new ArtifactMetadataRetrievalException("Error retrieving metadata", e, a); + } + } + + return new ResolutionGroup(artifact, dependencies, remoteRepositories); + } + + @Override + public List retrieveAvailableVersions( + Artifact artifact, ArtifactRepository localRepository, List remoteRepositories) + throws ArtifactMetadataRetrievalException { + throw new UnsupportedOperationException("Cannot get available versions in this test case"); + } + + @Override + public List retrieveAvailableVersionsFromDeploymentRepository( + Artifact artifact, ArtifactRepository localRepository, ArtifactRepository remoteRepository) + throws ArtifactMetadataRetrievalException { + throw new UnsupportedOperationException("Cannot get available versions in this test case"); + } + + @Override + public ResolutionGroup retrieve(MetadataResolutionRequest request) throws ArtifactMetadataRetrievalException { + return retrieve(request.getArtifact(), request.getLocalRepository(), request.getRemoteRepositories()); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/toolchain/DefaultToolchainManagerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/toolchain/DefaultToolchainManagerTest.java new file mode 100644 index 000000000000..18de14738221 --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/toolchain/DefaultToolchainManagerTest.java @@ -0,0 +1,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.api.Session; +import org.apache.maven.api.services.Lookup; +import org.apache.maven.api.services.ToolchainFactory; +import org.apache.maven.api.toolchain.ToolchainModel; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class DefaultToolchainManagerTest { + // Mocks to inject into toolchainManager + @Mock + private Logger logger; + + private ToolchainManagerFactory.DefaultToolchainManagerV3 toolchainManager; + + @Mock + private ToolchainFactory toolchainFactoryBasicType; + + @Mock + private ToolchainFactory toolchainFactoryRareType; + + @Mock + private Lookup lookup; + + @BeforeEach + void onSetup() throws Exception { + MockitoAnnotations.initMocks(this); + + Map factories = new HashMap<>(); + factories.put("basic", toolchainFactoryBasicType); + factories.put("rare", toolchainFactoryRareType); + + when(lookup.lookupMap(ToolchainFactory.class)).thenReturn(factories); + when(lookup.lookupMap(org.apache.maven.toolchain.ToolchainFactory.class)) + .thenReturn(Map.of()); + + toolchainManager = new ToolchainManagerFactory(lookup, logger).v3Manager(); + } + + @Test + void testNoModels() { + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest(); + when(session.getRequest()).thenReturn(executionRequest); + + List toolchains = toolchainManager.getToolchains(session, "unknown", null); + + assertEquals(0, toolchains.size()); + } + + @Test + void testModelNoFactory() { + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest(); + List toolchainModels = new ArrayList<>(); + toolchainModels.add(ToolchainModel.newBuilder().type("unknown").build()); + when(session.getRequest()).thenReturn(executionRequest); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + when(sessionv4.getToolchains()).thenReturn(toolchainModels); + + List toolchains = toolchainManager.getToolchains(session, "unknown", null); + + assertEquals(0, toolchains.size()); + verify(logger).error("Missing toolchain factory for type: unknown. Possibly caused by misconfigured project."); + } + + @Test + void testModelAndFactory() { + MavenSession session = mock(MavenSession.class); + List toolchainModels = List.of( + ToolchainModel.newBuilder().type("basic").build(), + ToolchainModel.newBuilder().type("basic").build(), + ToolchainModel.newBuilder().type("rare").build()); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + when(sessionv4.getToolchains()).thenReturn(toolchainModels); + + org.apache.maven.api.Toolchain rareToolchain = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryRareType.createToolchain(any())).thenReturn(rareToolchain); + + List toolchains = toolchainManager.getToolchains(session, "rare", null); + + assertEquals(1, toolchains.size()); + } + + @Test + void testModelsAndFactory() { + MavenSession session = mock(MavenSession.class); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + when(sessionv4.getToolchains()) + .thenReturn(List.of( + ToolchainModel.newBuilder().type("basic").build(), + ToolchainModel.newBuilder().type("basic").build(), + ToolchainModel.newBuilder().type("rare").build())); + + org.apache.maven.api.Toolchain basicToolchain = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryBasicType.createToolchain(any())).thenReturn(basicToolchain); + org.apache.maven.api.Toolchain basicToolchain2 = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryBasicType.createToolchain(any())).thenReturn(basicToolchain2); + + List toolchains = toolchainManager.getToolchains(session, "basic", null); + + assertEquals(2, toolchains.size()); + } + + @Test + void testRequirements() throws Exception { + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest(); + when(session.getRequest()).thenReturn(executionRequest); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + when(sessionv4.getToolchains()) + .thenReturn(List.of( + ToolchainModel.newBuilder().type("basic").build(), + ToolchainModel.newBuilder().type("rare").build())); + + org.apache.maven.api.Toolchain basicPrivate = mock(org.apache.maven.api.Toolchain.class); + when(basicPrivate.matchesRequirements(anyMap())).thenReturn(false); + when(basicPrivate.matchesRequirements(ArgumentMatchers.eq(Map.of("key", "value")))) + .thenReturn(true); + when(toolchainFactoryBasicType.createToolchain(isA(org.apache.maven.api.toolchain.ToolchainModel.class))) + .thenReturn(basicPrivate); + + List toolchains = + toolchainManager.getToolchains(session, "basic", Collections.singletonMap("key", "value")); + + assertEquals(1, toolchains.size()); + } + + @Test + void testToolchainsForAvailableType() throws Exception { + // prepare + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest req = new DefaultMavenExecutionRequest(); + when(session.getRequest()).thenReturn(req); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + + org.apache.maven.api.Toolchain basicToolchain = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryBasicType.createDefaultToolchain()).thenReturn(Optional.of(basicToolchain)); + org.apache.maven.api.Toolchain rareToolchain = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryRareType.createDefaultToolchain()).thenReturn(Optional.of(rareToolchain)); + + // execute + ToolchainPrivate[] toolchains = toolchainManager.getToolchainsForType("basic", session); + + // verify + verify(logger, never()).error(anyString()); + assertEquals(1, toolchains.length); + } + + @Test + void testToolchainsForUnknownType() throws Exception { + // prepare + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest req = new DefaultMavenExecutionRequest(); + when(session.getRequest()).thenReturn(req); + + org.apache.maven.api.Toolchain basicToolchain = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryBasicType.createDefaultToolchain()).thenReturn(Optional.of(basicToolchain)); + org.apache.maven.api.Toolchain rareToolchain = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryRareType.createDefaultToolchain()).thenReturn(Optional.of(rareToolchain)); + + // execute + ToolchainPrivate[] toolchains = toolchainManager.getToolchainsForType("unknown", session); + + // verify + verify(logger).error("Missing toolchain factory for type: unknown. Possibly caused by misconfigured project."); + assertEquals(0, toolchains.length); + } + + @Test + void testToolchainsForConfiguredType() throws Exception { + // prepare + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest req = new DefaultMavenExecutionRequest(); + when(session.getRequest()).thenReturn(req); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + List toolchainModels = new ArrayList<>(); + when(sessionv4.getToolchains()).thenReturn(toolchainModels); + + ToolchainModel basicToolchainModel = + ToolchainModel.newBuilder().type("basic").build(); + toolchainModels.add(basicToolchainModel); + toolchainModels.add(basicToolchainModel); + + ToolchainModel rareToolchainModel = + ToolchainModel.newBuilder().type("rare").build(); + toolchainModels.add(rareToolchainModel); + + org.apache.maven.api.Toolchain basic = mock(org.apache.maven.api.Toolchain.class); + when(toolchainFactoryBasicType.createToolchain(basicToolchainModel)).thenReturn(basic); + + // execute + ToolchainPrivate[] toolchains = toolchainManager.getToolchainsForType("basic", session); + + // verify + verify(logger, never()).error(anyString()); + assertEquals(2, toolchains.length); + } + + @Test + void testMisconfiguredToolchain() throws Exception { + // prepare + MavenSession session = mock(MavenSession.class); + MavenExecutionRequest req = new DefaultMavenExecutionRequest(); + when(session.getRequest()).thenReturn(req); + Session sessionv4 = mock(Session.class); + when(session.getSession()).thenReturn(sessionv4); + + // execute + ToolchainPrivate[] basics = toolchainManager.getToolchainsForType("basic", session); + + // verify + assertEquals(0, basics.length); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/toolchain/DefaultToolchainTest.java b/compat/maven-compat/src/test/java/org/apache/maven/toolchain/DefaultToolchainTest.java new file mode 100644 index 000000000000..d6efd846dfac --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/toolchain/DefaultToolchainTest.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import java.util.Collections; + +import org.apache.maven.toolchain.java.DefaultJavaToolChain; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class DefaultToolchainTest { + private final Logger logger = mock(Logger.class); + + @BeforeEach + void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + private DefaultToolchain newDefaultToolchain(ToolchainModel model) { + return new DefaultToolchain(model, logger) { + @Override + public String findTool(String toolName) { + return null; + } + }; + } + + private DefaultToolchain newDefaultToolchain(ToolchainModel model, String type) { + return new DefaultToolchain(model, type, logger) { + @Override + public String findTool(String toolName) { + return null; + } + }; + } + + @Test + void testGetModel() { + ToolchainModel model = new ToolchainModel(); + DefaultToolchain toolchain = newDefaultToolchain(model); + assertEquals(model, toolchain.getModel()); + } + + @Test + void testGetType() { + ToolchainModel model = new ToolchainModel(); + DefaultToolchain toolchain = newDefaultToolchain(model, "TYPE"); + assertEquals("TYPE", toolchain.getType()); + + model.setType("MODEL_TYPE"); + toolchain = newDefaultToolchain(model); + assertEquals("MODEL_TYPE", toolchain.getType()); + } + + @Test + void testGetLogger() { + ToolchainModel model = new ToolchainModel(); + DefaultToolchain toolchain = newDefaultToolchain(model); + assertEquals(logger, toolchain.getLog()); + } + + @Test + void testMissingRequirementProperty() { + ToolchainModel model = new ToolchainModel(); + model.setType("TYPE"); + DefaultToolchain toolchain = newDefaultToolchain(model); + + assertFalse(toolchain.matchesRequirements(Collections.singletonMap("name", "John Doe"))); + verify(logger).debug("Toolchain {} is missing required property: {}", toolchain, "name"); + } + + @Test + void testNonMatchingRequirementProperty() { + ToolchainModel model = new ToolchainModel(); + model.setType("TYPE"); + DefaultToolchain toolchain = newDefaultToolchain(model); + toolchain.addProvideToken("name", RequirementMatcherFactory.createExactMatcher("Jane Doe")); + + assertFalse(toolchain.matchesRequirements(Collections.singletonMap("name", "John Doe"))); + verify(logger).debug("Toolchain {} doesn't match required property: {}", toolchain, "name"); + } + + @Test + void testEquals() { + ToolchainModel tm1 = new ToolchainModel(); + tm1.setType("jdk"); + tm1.addProvide("version", "1.5"); + tm1.addProvide("vendor", "sun"); + Xpp3Dom configuration1 = new Xpp3Dom("configuration"); + Xpp3Dom jdkHome1 = new Xpp3Dom("jdkHome"); + jdkHome1.setValue("${env.JAVA_HOME}"); + configuration1.addChild(jdkHome1); + tm1.setConfiguration(configuration1); + + ToolchainModel tm2 = new ToolchainModel(); + tm1.setType("jdk"); + tm1.addProvide("version", "1.4"); + tm1.addProvide("vendor", "sun"); + Xpp3Dom configuration2 = new Xpp3Dom("configuration"); + Xpp3Dom jdkHome2 = new Xpp3Dom("jdkHome"); + jdkHome2.setValue("${env.JAVA_HOME}"); + configuration2.addChild(jdkHome2); + tm2.setConfiguration(configuration2); + + DefaultToolchain tc1 = new DefaultJavaToolChain(tm1, null); + DefaultToolchain tc2 = new DefaultJavaToolChain(tm2, null); + + assertEquals(tc1, tc1); + assertNotEquals(tc1, tc2); + assertNotEquals(tc2, tc1); + assertEquals(tc2, tc2); + } +} diff --git a/compat/maven-compat/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java b/compat/maven-compat/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java new file mode 100644 index 000000000000..7cb3b23ca0bb --- /dev/null +++ b/compat/maven-compat/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.toolchain; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * + */ +class RequirementMatcherFactoryTest { + + /** + * Test of createExactMatcher method, of class RequirementMatcherFactory. + */ + @Test + void testCreateExactMatcher() { + RequirementMatcher matcher; + matcher = RequirementMatcherFactory.createExactMatcher("foo"); + assertFalse(matcher.matches("bar")); + assertFalse(matcher.matches("foobar")); + assertFalse(matcher.matches("foob")); + assertTrue(matcher.matches("foo")); + } + + /** + * Test of createVersionMatcher method, of class RequirementMatcherFactory. + */ + @Test + void testCreateVersionMatcher() { + RequirementMatcher matcher; + matcher = RequirementMatcherFactory.createVersionMatcher("1.5.2"); + assertFalse(matcher.matches("1.5")); + assertTrue(matcher.matches("1.5.2")); + assertFalse(matcher.matches("[1.4,1.5)")); + assertFalse(matcher.matches("[1.5,1.5.2)")); + assertFalse(matcher.matches("(1.5.2,1.6)")); + assertTrue(matcher.matches("(1.4,1.5.2]")); + assertTrue(matcher.matches("(1.5,)")); + assertEquals("1.5.2", matcher.toString()); + + // Ensure it is not printed as 1.5.0 + matcher = RequirementMatcherFactory.createVersionMatcher("1.5"); + assertEquals("1.5", matcher.toString()); + } +} diff --git a/compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/jdk/jre/placeholder.txt b/compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/jdk/jre/placeholder.txt new file mode 100644 index 000000000000..4a9eb145a1e3 --- /dev/null +++ b/compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/jdk/jre/placeholder.txt @@ -0,0 +1 @@ +need it so that empty directory does not get deleted diff --git a/maven-core/src/test/projects/project-builder/it0063/jdk/lib/tools.jar b/compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/jdk/lib/tools.jar similarity index 100% rename from maven-core/src/test/projects/project-builder/it0063/jdk/lib/tools.jar rename to compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/jdk/lib/tools.jar diff --git a/maven-core/src/test/projects/project-builder/it0063/pom.xml b/compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/pom.xml similarity index 100% rename from maven-core/src/test/projects/project-builder/it0063/pom.xml rename to compat/maven-compat/src/test/projects/project-dependencies-resolver/it0063/pom.xml diff --git a/maven-core/src/test/projects/project-dependencies-resolver/project-with-exclusions/pom.xml b/compat/maven-compat/src/test/projects/project-dependencies-resolver/project-with-exclusions/pom.xml similarity index 100% rename from maven-core/src/test/projects/project-dependencies-resolver/project-with-exclusions/pom.xml rename to compat/maven-compat/src/test/projects/project-dependencies-resolver/project-with-exclusions/pom.xml diff --git a/maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.jar b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.jar similarity index 100% rename from maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.jar rename to compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.jar diff --git a/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.pom b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.pom new file mode 100644 index 000000000000..03caa12ee830 --- /dev/null +++ b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/0.1/a-0.1.pom @@ -0,0 +1,41 @@ + + + + + + 4.0.0 + + org.apache.maven.its + a + 0.1 + jar + + Maven Integration Test :: Dummy Artifact + + + + + + + maven-core-it + file:///${basedir}/repo + + + diff --git a/maven-compat/src/test/remote-repo/org/apache/maven/its/a/maven-metadata.xml b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/maven-metadata.xml similarity index 100% rename from maven-compat/src/test/remote-repo/org/apache/maven/its/a/maven-metadata.xml rename to compat/maven-compat/src/test/remote-repo/org/apache/maven/its/a/maven-metadata.xml diff --git a/maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.jar b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.jar similarity index 100% rename from maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.jar rename to compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.jar diff --git a/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.pom b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.pom new file mode 100644 index 000000000000..149a2410888d --- /dev/null +++ b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/0.1/b-0.1.pom @@ -0,0 +1,49 @@ + + + + + + 4.0.0 + + org.apache.maven.its + b + 0.1 + jar + + Maven Integration Test :: Dummy Artifact + + + + + + + maven-core-it + file:///${basedir}/repo + + + + + + org.apache.maven.its + a + 0.1 + + + diff --git a/maven-compat/src/test/remote-repo/org/apache/maven/its/b/maven-metadata.xml b/compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/maven-metadata.xml similarity index 100% rename from maven-compat/src/test/remote-repo/org/apache/maven/its/b/maven-metadata.xml rename to compat/maven-compat/src/test/remote-repo/org/apache/maven/its/b/maven-metadata.xml diff --git a/maven-compat/src/test/repository-system/maven-core-2.1.0.jar b/compat/maven-compat/src/test/repository-system/maven-core-2.1.0.jar similarity index 100% rename from maven-compat/src/test/repository-system/maven-core-2.1.0.jar rename to compat/maven-compat/src/test/repository-system/maven-core-2.1.0.jar diff --git a/compat/maven-compat/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject b/compat/maven-compat/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject new file mode 100644 index 000000000000..fb476677f3a5 --- /dev/null +++ b/compat/maven-compat/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject @@ -0,0 +1 @@ +org.apache.maven.project.EmptyLifecycleBindingsInjector diff --git a/maven-compat/src/test/resources/artifact-install/artifact-1.0.jar b/compat/maven-compat/src/test/resources/artifact-install/artifact-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/artifact-install/artifact-1.0.jar rename to compat/maven-compat/src/test/resources/artifact-install/artifact-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p0-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p0-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p0-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p0-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p1-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p1-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p1-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p1-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p2-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p2-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p2-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p2-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p3-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p3-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p3-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p3-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p4-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p4-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p4-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t00/maven/poms/p4-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p0-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p0-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p0-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p0-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p1-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p1-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p1-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p1-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p2-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p2-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p2-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p2-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p3-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p3-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p3-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p3-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p4-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p4-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p4-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t01/maven.t01/poms/p4-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/p5/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/p5/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/p5/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/p5/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/p4/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/p3/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/p2/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t02/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t02/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t02/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t03/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t03/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t03/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t03/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t03/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t03/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t03/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t03/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-2.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-2.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-2.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-b-2.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-2.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-2.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-2.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/jars/t04-c-2.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-2.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-2.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-2.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-b-2.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-2.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-2.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-2.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/maven-test/poms/t04-c-2.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t04/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t04/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t04/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t04/p0/p1/pom.xml diff --git a/compat/maven-compat/src/test/resources/inheritance-repo/t04/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t04/p0/pom.xml new file mode 100644 index 000000000000..b7eb670ec6aa --- /dev/null +++ b/compat/maven-compat/src/test/resources/inheritance-repo/t04/p0/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + maven-t04 + p0 + pom + p0 + 1.0 + + Codehaus + + + + + + + + maven-test + t04-a + 1.0 + + + + maven-test + t04-b + 1.0 + + + + maven-test + t04-c + 2.0 + + + + + + diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-2.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-2.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-2.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-a-2.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-2.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-2.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-2.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-b-2.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.2.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.2.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.2.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/jars/t05-d-1.2.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-2.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-2.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-2.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-a-2.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-2.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-2.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-2.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-b-2.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.2.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.2.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.2.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/maven-test/poms/t05-d-1.2.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t05/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t05/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t05/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t05/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t05/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-b-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.2.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.2.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.2.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/jars/t06-d-1.2.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-b-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.2.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.2.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.2.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/maven-test/poms/t06-d-1.2.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t06/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t06/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t06/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t06/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t06/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-b-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.2.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.2.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.2.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/jars/t07-d-1.2.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-b-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.2.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.2.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.2.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/maven-test/poms/t07-d-1.2.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t07/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t07/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t07/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t07/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t07/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-b-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.1.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.1.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.1.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.1.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.2.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.2.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.2.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/jars/t08-d-1.2.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-b-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.1.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.1.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.1.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.2.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.2.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.2.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/maven-test/poms/t08-d-1.2.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t08/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t08/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t08/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t08/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t08/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-d-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-d-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-d-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/jars/t09-d-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-d-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-d-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-d-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/maven-test/poms/t09-d-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t09/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/p0/p2/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t09/p0/p2/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/p0/p2/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/p0/p2/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t09/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t09/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t09/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t09/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-a-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-a-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-a-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-b-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-b-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-b-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-c-1.0.jar b/compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-c-1.0.jar rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/jars/t10-c-1.0.jar diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-a-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-a-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-a-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-b-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-b-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-b-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-c-1.0.pom b/compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-c-1.0.pom rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/maven-test/poms/t10-c-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t10/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t10/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t10/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t10/p0/p1/pom.xml diff --git a/compat/maven-compat/src/test/resources/inheritance-repo/t10/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t10/p0/pom.xml new file mode 100644 index 000000000000..74b631756cea --- /dev/null +++ b/compat/maven-compat/src/test/resources/inheritance-repo/t10/p0/pom.xml @@ -0,0 +1,41 @@ + + 4.0.0 + maven-t10 + p0 + pom + p0 + 1.0 + + Codehaus + + + + + + + + maven-test + t10-a + 1.0 + test + + + + maven-test + t10-b + 1.0 + runtime + + + + maven-test + t10-c + 1.0 + test + + + + + + + diff --git a/maven-compat/src/test/resources/inheritance-repo/t11/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t11/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t11/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t11/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t11/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t11/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t11/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t11/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t12/p0/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t12/p0/p1/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t12/p0/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t12/p0/p1/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t12/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t12/p0/pom.xml similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t12/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t12/p0/pom.xml diff --git a/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/maven/p0/1.0/p0-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t12scm/p0/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t12scm/maven/p0/1.0/p0-1.0.pom diff --git a/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/modules/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/maven/p1/1.0/p1-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/inheritance-repo/t12scm/p0/modules/p1/pom.xml rename to compat/maven-compat/src/test/resources/inheritance-repo/t12scm/maven/p1/1.0/p1-1.0.pom diff --git a/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/modules/p1/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/modules/p1/pom.xml new file mode 100644 index 000000000000..736b51539b45 --- /dev/null +++ b/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/modules/p1/pom.xml @@ -0,0 +1,11 @@ + + + p0 + maven + 1.0 + ../../pom.xml + + 4.0.0 + p1 + + diff --git a/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/pom.xml b/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/pom.xml new file mode 100644 index 000000000000..a22df68eac12 --- /dev/null +++ b/compat/maven-compat/src/test/resources/inheritance-repo/t12scm/p0/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + maven + p0 + pom + 1.0 + + + scm:svn:http://host/p0 + scm:svn:https://host/p0 + http://host/viewer?path=/p0 + + + + modules/p1 + + + diff --git a/maven-compat/src/test/resources/local-repo/marker.txt b/compat/maven-compat/src/test/resources/local-repo/marker.txt similarity index 100% rename from maven-compat/src/test/resources/local-repo/marker.txt rename to compat/maven-compat/src/test/resources/local-repo/marker.txt diff --git a/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-a-1.0.jar b/compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-a-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-a-1.0.jar rename to compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-a-1.0.jar diff --git a/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-b-1.0.jar b/compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-b-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-b-1.0.jar rename to compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-b-1.0.jar diff --git a/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-c-1.0.jar b/compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-c-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-c-1.0.jar rename to compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-c-1.0.jar diff --git a/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-d-1.0.jar b/compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-d-1.0.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-d-1.0.jar rename to compat/maven-compat/src/test/resources/local-repo/maven-test/jars/maven-test-d-1.0.jar diff --git a/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-a-1.0.pom b/compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-a-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-a-1.0.pom rename to compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-a-1.0.pom diff --git a/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-b-1.0.pom b/compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-b-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-b-1.0.pom rename to compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-b-1.0.pom diff --git a/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-c-1.0.pom b/compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-c-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-c-1.0.pom rename to compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-c-1.0.pom diff --git a/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.1.pom b/compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.1.pom similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.1.pom rename to compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.1.pom diff --git a/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.2.pom b/compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.2.pom similarity index 100% rename from maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.2.pom rename to compat/maven-compat/src/test/resources/local-repo/maven-test/poms/maven-test-d-1.2.pom diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar.snapshot-version b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar.snapshot-version similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar.snapshot-version rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-a-1.0-SNAPSHOT.jar.snapshot-version diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar.snapshot-version b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar.snapshot-version similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar.snapshot-version rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-b-1.0-SNAPSHOT.jar.snapshot-version diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar.snapshot-version b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar.snapshot-version similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar.snapshot-version rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/jars/maven-snapshot-e-1.0-SNAPSHOT.jar.snapshot-version diff --git a/maven-compat/src/test/resources/local-repo/snapshot-test/poms/maven-test-snapshot-resolving-1.0.pom b/compat/maven-compat/src/test/resources/local-repo/snapshot-test/poms/maven-test-snapshot-resolving-1.0.pom similarity index 100% rename from maven-compat/src/test/resources/local-repo/snapshot-test/poms/maven-test-snapshot-resolving-1.0.pom rename to compat/maven-compat/src/test/resources/local-repo/snapshot-test/poms/maven-test-snapshot-resolving-1.0.pom diff --git a/maven-compat/src/test/resources/org/apache/maven/artifact/deployer/ArtifactDeployerTest.xml b/compat/maven-compat/src/test/resources/org/apache/maven/artifact/deployer/ArtifactDeployerTest.xml similarity index 100% rename from maven-compat/src/test/resources/org/apache/maven/artifact/deployer/ArtifactDeployerTest.xml rename to compat/maven-compat/src/test/resources/org/apache/maven/artifact/deployer/ArtifactDeployerTest.xml diff --git a/maven-compat/src/test/resources/org/apache/maven/artifact/installer/ArtifactInstallerTest.xml b/compat/maven-compat/src/test/resources/org/apache/maven/artifact/installer/ArtifactInstallerTest.xml similarity index 100% rename from maven-compat/src/test/resources/org/apache/maven/artifact/installer/ArtifactInstallerTest.xml rename to compat/maven-compat/src/test/resources/org/apache/maven/artifact/installer/ArtifactInstallerTest.xml diff --git a/maven-compat/src/test/resources/org/apache/maven/artifact/manager/DefaultWagonManagerTest.xml b/compat/maven-compat/src/test/resources/org/apache/maven/artifact/manager/DefaultWagonManagerTest.xml similarity index 100% rename from maven-compat/src/test/resources/org/apache/maven/artifact/manager/DefaultWagonManagerTest.xml rename to compat/maven-compat/src/test/resources/org/apache/maven/artifact/manager/DefaultWagonManagerTest.xml diff --git a/maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactResolverTest.xml b/compat/maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactResolverTest.xml similarity index 100% rename from maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactResolverTest.xml rename to compat/maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactResolverTest.xml diff --git a/maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactUpdatePolicyTest.xml b/compat/maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactUpdatePolicyTest.xml similarity index 100% rename from maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactUpdatePolicyTest.xml rename to compat/maven-compat/src/test/resources/org/apache/maven/artifact/resolver/ArtifactUpdatePolicyTest.xml diff --git a/compat/maven-compat/src/test/resources/pom.xml b/compat/maven-compat/src/test/resources/pom.xml new file mode 100644 index 000000000000..bad0e6e23e21 --- /dev/null +++ b/compat/maven-compat/src/test/resources/pom.xml @@ -0,0 +1,155 @@ + + + + + + 4.0.0 + org.apache.maven + maven-core + jar + Maven + 2.0-SNAPSHOT + 2001 + + + org.apache.maven + maven-model + 2.0-SNAPSHOT + jar + compile + + + org.apache.maven + maven-plugin + 2.0-SNAPSHOT + jar + compile + + + commons-cli + commons-cli + 1.0-beta-2 + jar + compile + + + plexus + plexus-i18n + 1.0-beta-2-SNAPSHOT + jar + compile + + + ognl + ognl + 3.0.12 + jar + compile + + + marmalade + marmalade-core + 0.1 + jar + compile + + + marmalade + marmalade-el-ognl + 0.1 + jar + compile + + + plexus + plexus-compiler-api + 1.0 + jar + compile + + + plexus + plexus-compiler-javac + 1.0 + jar + compile + + + surefire + surefire-booter + 1.1 + jar + compile + + + maven + wagon-api + 0.9-SNAPSHOT + jar + compile + + + org.apache.maven + wagon-http-lightweight + 0.9-SNAPSHOT + jar + compile + + + maven + wagon-ssh + 0.9-SNAPSHOT + jar + compile + + + jsch + jsch + 0.1.14 + jar + compile + + + qdox + qdox + 1.2 + jar + compile + + + diff --git a/maven-compat/src/test/resources/projects/scope/project-with-scoped-dependencies.xml b/compat/maven-compat/src/test/resources/projects/scope/project-with-scoped-dependencies.xml similarity index 100% rename from maven-compat/src/test/resources/projects/scope/project-with-scoped-dependencies.xml rename to compat/maven-compat/src/test/resources/projects/scope/project-with-scoped-dependencies.xml diff --git a/maven-compat/src/test/resources/projects/scope/transitive-compile-dep.xml b/compat/maven-compat/src/test/resources/projects/scope/transitive-compile-dep.xml similarity index 100% rename from maven-compat/src/test/resources/projects/scope/transitive-compile-dep.xml rename to compat/maven-compat/src/test/resources/projects/scope/transitive-compile-dep.xml diff --git a/maven-compat/src/test/resources/projects/scope/transitive-default-dep.xml b/compat/maven-compat/src/test/resources/projects/scope/transitive-default-dep.xml similarity index 100% rename from maven-compat/src/test/resources/projects/scope/transitive-default-dep.xml rename to compat/maven-compat/src/test/resources/projects/scope/transitive-default-dep.xml diff --git a/maven-compat/src/test/resources/projects/scope/transitive-provided-dep.xml b/compat/maven-compat/src/test/resources/projects/scope/transitive-provided-dep.xml similarity index 100% rename from maven-compat/src/test/resources/projects/scope/transitive-provided-dep.xml rename to compat/maven-compat/src/test/resources/projects/scope/transitive-provided-dep.xml diff --git a/maven-compat/src/test/resources/projects/scope/transitive-runtime-dep.xml b/compat/maven-compat/src/test/resources/projects/scope/transitive-runtime-dep.xml similarity index 100% rename from maven-compat/src/test/resources/projects/scope/transitive-runtime-dep.xml rename to compat/maven-compat/src/test/resources/projects/scope/transitive-runtime-dep.xml diff --git a/maven-compat/src/test/resources/projects/scope/transitive-test-dep.xml b/compat/maven-compat/src/test/resources/projects/scope/transitive-test-dep.xml similarity index 100% rename from maven-compat/src/test/resources/projects/scope/transitive-test-dep.xml rename to compat/maven-compat/src/test/resources/projects/scope/transitive-test-dep.xml diff --git a/compat/maven-embedder/pom.xml b/compat/maven-embedder/pom.xml new file mode 100644 index 000000000000..b1136b19c845 --- /dev/null +++ b/compat/maven-embedder/pom.xml @@ -0,0 +1,238 @@ + + + + 4.0.0 + + + org.apache.maven + maven-compat-modules + 4.1.0-SNAPSHOT + + + maven-embedder + + Maven Embedder (deprecated) + Maven embeddable component, with CLI and logging support. + + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-core + + + org.apache.maven + maven-api-cli + + + org.apache.maven + maven-api-model + + + org.apache.maven + maven-api-xml + + + + + org.apache.maven + maven-core + + + org.apache.maven + maven-cli + + + org.apache.maven + maven-di + + + org.apache.maven + maven-jline + + + org.apache.maven + maven-logging + + + org.apache.maven + maven-xml + + + + + org.apache.maven + maven-artifact + + + org.apache.maven + maven-builder-support + + + org.apache.maven + maven-model + + + org.apache.maven + maven-model-builder + + + org.apache.maven + maven-plugin-api + + + + org.apache.maven + maven-settings + + + org.apache.maven + maven-settings-builder + + + org.apache.maven + maven-toolchain-builder + + + org.apache.maven + maven-toolchain-model + + + + org.apache.maven.resolver + maven-resolver-api + + + org.apache.maven.resolver + maven-resolver-spi + + + org.apache.maven.resolver + maven-resolver-util + + + org.apache.maven.resolver + maven-resolver-impl + + + + org.codehaus.plexus + plexus-utils + + + org.codehaus.plexus + plexus-xml + + + + org.codehaus.plexus + plexus-classworlds + + + org.codehaus.plexus + plexus-sec-dispatcher + + + + org.slf4j + slf4j-api + + + commons-cli + commons-cli + + + + ch.qos.logback + logback-classic + true + + + org.slf4j + slf4j-simple + true + + + org.jline + jansi-core + + + + javax.inject + javax.inject + provided + + + org.eclipse.sisu + org.eclipse.sisu.plexus + provided + + + org.eclipse.sisu + org.eclipse.sisu.inject + provided + + + com.google.inject + guice + classes + provided + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.hamcrest + hamcrest + test + + + org.mockito + mockito-core + test + + + com.google.jimfs + jimfs + test + + + + + + + org.eclipse.sisu + sisu-maven-plugin + + + + diff --git a/compat/maven-embedder/src/examples/simple-project/pom.xml b/compat/maven-embedder/src/examples/simple-project/pom.xml new file mode 100644 index 000000000000..0f8deb0dadba --- /dev/null +++ b/compat/maven-embedder/src/examples/simple-project/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + org.apache.maven.embedder + simple-project + 1.0-SNAPSHOT + simple-project + http://maven.apache.org + + + junit + junit + 4.13.1 + test + + + + development + + diff --git a/compat/maven-embedder/src/examples/simple-project/settings.xml b/compat/maven-embedder/src/examples/simple-project/settings.xml new file mode 100644 index 000000000000..63cf96e1fd0f --- /dev/null +++ b/compat/maven-embedder/src/examples/simple-project/settings.xml @@ -0,0 +1,26 @@ + + + + + + org.codehaus.tycho + org.sonatype.pwt + + diff --git a/compat/maven-embedder/src/examples/simple-project/src/main/java/org/apache/maven/embedder/App.java b/compat/maven-embedder/src/examples/simple-project/src/main/java/org/apache/maven/embedder/App.java new file mode 100644 index 000000000000..f30f45c756b7 --- /dev/null +++ b/compat/maven-embedder/src/examples/simple-project/src/main/java/org/apache/maven/embedder/App.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.embedder; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/compat/maven-embedder/src/examples/simple-project/src/test/java/org/apache/maven/embedder/AppTest.java b/compat/maven-embedder/src/examples/simple-project/src/test/java/org/apache/maven/embedder/AppTest.java new file mode 100644 index 000000000000..f51498061130 --- /dev/null +++ b/compat/maven-embedder/src/examples/simple-project/src/test/java/org/apache/maven/embedder/AppTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.embedder; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigorous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java new file mode 100644 index 000000000000..3d930b10cf73 --- /dev/null +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java @@ -0,0 +1,434 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.cli; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.DeprecatedAttributes; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.maven.jline.MessageUtils; + +/** + */ +@Deprecated +public class CLIManager { + public static final char ALTERNATE_POM_FILE = 'f'; + + public static final char BATCH_MODE = 'B'; + + public static final String NON_INTERACTIVE = "non-interactive"; + + public static final String FORCE_INTERACTIVE = "force-interactive"; + + public static final char SET_USER_PROPERTY = 'D'; + + /** + * @deprecated Use {@link #SET_USER_PROPERTY} + */ + @Deprecated + public static final char SET_SYSTEM_PROPERTY = SET_USER_PROPERTY; + + public static final char OFFLINE = 'o'; + + public static final char QUIET = 'q'; + + public static final char VERBOSE = 'X'; + + public static final char ERRORS = 'e'; + + public static final char HELP = 'h'; + + public static final char VERSION = 'v'; + + public static final char SHOW_VERSION = 'V'; + + public static final char NON_RECURSIVE = 'N'; + + public static final char UPDATE_SNAPSHOTS = 'U'; + + public static final char ACTIVATE_PROFILES = 'P'; + + public static final String SUPPRESS_SNAPSHOT_UPDATES = "nsu"; + + public static final char CHECKSUM_FAILURE_POLICY = 'C'; + + public static final char CHECKSUM_WARNING_POLICY = 'c'; + + public static final char ALTERNATE_USER_SETTINGS = 's'; + + public static final String ALTERNATE_PROJECT_SETTINGS = "ps"; + + @Deprecated + public static final String ALTERNATE_GLOBAL_SETTINGS = "gs"; + + public static final String ALTERNATE_INSTALLATION_SETTINGS = "is"; + + public static final char ALTERNATE_USER_TOOLCHAINS = 't'; + + @Deprecated + public static final String ALTERNATE_GLOBAL_TOOLCHAINS = "gt"; + + public static final String ALTERNATE_INSTALLATION_TOOLCHAINS = "it"; + + public static final String FAIL_FAST = "ff"; + + public static final String FAIL_ON_SEVERITY = "fos"; + + public static final String FAIL_AT_END = "fae"; + + public static final String FAIL_NEVER = "fn"; + + public static final String RESUME = "r"; + + public static final String RESUME_FROM = "rf"; + + public static final String PROJECT_LIST = "pl"; + + public static final String ALSO_MAKE = "am"; + + public static final String ALSO_MAKE_DEPENDENTS = "amd"; + + public static final String LOG_FILE = "l"; + + public static final String ENCRYPT_MASTER_PASSWORD = "emp"; + + public static final String ENCRYPT_PASSWORD = "ep"; + + public static final String THREADS = "T"; + + public static final String BUILDER = "b"; + + public static final String NO_TRANSFER_PROGRESS = "ntp"; + + public static final String COLOR = "color"; + + public static final String CACHE_ARTIFACT_NOT_FOUND = "canf"; + + public static final String STRICT_ARTIFACT_DESCRIPTOR_POLICY = "sadp"; + + public static final String IGNORE_TRANSITIVE_REPOSITORIES = "itr"; + + public static final String DEBUG = "debug"; + public static final String ENC = "enc"; + public static final String YJP = "yjp"; + + protected Options options; + protected final Set