From db22fbfbd98a8ae30c05fcc619cc74f0ee25279e Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 2 Jun 2023 10:03:17 +0200 Subject: [PATCH 01/22] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 34360b9..9ab69bb 100644 --- a/pom.xml +++ b/pom.xml @@ -21,12 +21,12 @@ See the Apache License Version 2.0 for the specific language governing permissio plexus-build-api - 1.2.0 + 1.2.1-SNAPSHOT scm:git:https://github.com/codehaus-plexus/plexus-build-api.git ${project.scm.connection} - plexus-build-api-1.2.0 + master https://github.com/codehaus-plexus/plexus-build-api/tree/master From 3d46b7a8c6cce2ac900f41910161fd2d8c7e0045 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 02:58:08 +0000 Subject: [PATCH 02/22] Bump maven-surefire-plugin from 3.1.0 to 3.1.2 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.0...surefire-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ab69bb..0d4c7b7 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.apache.maven.plugins maven-surefire-plugin - 3.1.0 + 3.1.2 true From 2098dfd2633ff450714b114717d35de7f3ffa0a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 02:52:46 +0000 Subject: [PATCH 03/22] Bump org.codehaus.plexus:plexus from 13 to 14 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 13 to 14. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0d4c7b7..c2b9e39 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 13 + 14 plexus-build-api From 62478dbf48bf812963818ad946ff9491f9cc655a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 02:32:52 +0000 Subject: [PATCH 04/22] Bump org.codehaus.plexus:plexus from 14 to 15 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 14 to 15. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c2b9e39..aff7274 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 14 + 15 plexus-build-api From d7055dc45530a679b8f1ff62c0aa5562101d40a7 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Wed, 4 Oct 2023 18:57:40 +0200 Subject: [PATCH 05/22] Cleanup after pom update --- pom.xml | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index aff7274..83c4112 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ See the Apache License Version 2.0 for the specific language governing permissio slf4j-api 1.7.36 - + org.sonatype.plexus plexus-build-api @@ -83,29 +83,10 @@ See the Apache License Version 2.0 for the specific language governing permissio org.eclipse.sisu sisu-maven-plugin - 0.9.0.M2 - - - index-project - - main-index - test-index - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - org.apache.maven.plugins maven-surefire-plugin - 3.1.2 true From 959ebc999e23a0474dff4f72e5cf8ced6df4fce3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 02:36:30 +0000 Subject: [PATCH 06/22] Bump org.codehaus.plexus:plexus from 15 to 16 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 15 to 16. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83c4112..6ecef5d 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 15 + 16 plexus-build-api From 48fe7eab8d45fd96264f043d4963c6613075ef99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 02:52:03 +0000 Subject: [PATCH 07/22] Bump release-drafter/release-drafter from 5 to 6 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5 to 6. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v5...v6) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 4000f8c..4c09c8a 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -7,6 +7,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c1ea4cf8780d234949254475c1feae7a14418ed6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 02:23:20 +0000 Subject: [PATCH 08/22] Bump org.codehaus.plexus:plexus from 16 to 17 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 16 to 17. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ecef5d..02114dc 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 16 + 17 plexus-build-api From 178a7588ce24e5712b99d72d862ca49605207da4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 02:31:24 +0000 Subject: [PATCH 09/22] Bump org.codehaus.plexus:plexus-utils from 4.0.0 to 4.0.1 Bumps [org.codehaus.plexus:plexus-utils](https://github.com/codehaus-plexus/plexus-utils) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/codehaus-plexus/plexus-utils/releases) - [Commits](https://github.com/codehaus-plexus/plexus-utils/compare/plexus-utils-4.0.0...plexus-utils-4.0.1) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus-utils dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 02114dc..d623c70 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus-utils - 4.0.0 + 4.0.1 javax.inject From a6af31164f4732fd1216c049e94c9d6829b6b447 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 22:22:29 +0200 Subject: [PATCH 10/22] Bump org.codehaus.plexus:plexus from 17 to 18 (#82) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d623c70..4b90986 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 17 + 18 plexus-build-api From 1d79201aed2fa2d9ef102d8c0e18a7000cc2b140 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 02:02:51 +0000 Subject: [PATCH 11/22] Bump org.eclipse.sisu:org.eclipse.sisu.plexus from 0.9.0.M2 to 0.9.0.M3 Bumps [org.eclipse.sisu:org.eclipse.sisu.plexus](https://github.com/eclipse/sisu.inject) from 0.9.0.M2 to 0.9.0.M3. - [Release notes](https://github.com/eclipse/sisu.inject/releases) - [Changelog](https://github.com/eclipse-sisu/sisu-project/blob/main/RELEASE.md) - [Commits](https://github.com/eclipse/sisu.inject/compare/milestones/0.9.0.M2...milestones/0.9.0.M3) --- updated-dependencies: - dependency-name: org.eclipse.sisu:org.eclipse.sisu.plexus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b90986..0d45c93 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.eclipse.sisu org.eclipse.sisu.plexus - 0.9.0.M2 + 0.9.0.M3 * From 745367c774c2d77f9c858e701cec39bd1d5ce02c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 02:41:01 +0000 Subject: [PATCH 12/22] Bump org.codehaus.plexus:plexus-utils from 4.0.1 to 4.0.2 Bumps [org.codehaus.plexus:plexus-utils](https://github.com/codehaus-plexus/plexus-utils) from 4.0.1 to 4.0.2. - [Release notes](https://github.com/codehaus-plexus/plexus-utils/releases) - [Commits](https://github.com/codehaus-plexus/plexus-utils/compare/plexus-utils-4.0.1...plexus-utils-4.0.2) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus-utils dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0d45c93..bb66ecc 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus-utils - 4.0.1 + 4.0.2 javax.inject From 827c422448580fdb8bad178c5d90e00790113dda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:08:02 +0000 Subject: [PATCH 13/22] Bump org.codehaus.plexus:plexus from 18 to 19 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 18 to 19. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb66ecc..94fb9a8 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 18 + 19 plexus-build-api From 747f1d5b4ac601bb448626d983adfa5c51d79dce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 02:15:02 +0000 Subject: [PATCH 14/22] Bump org.codehaus.plexus:plexus from 19 to 20 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 19 to 20. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 94fb9a8..f332246 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 19 + 20 plexus-build-api From b4dbb226b5db365aa13c5aeaf4890ed2d29e6345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 22 Feb 2025 05:39:53 +0100 Subject: [PATCH 15/22] Add a connect API for inter-process-communication between maven and IDE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently an IDE launching an external maven process is rather blind, it can only see if the process exit with success and maybe read the standard stream. A much more feature rich experience could been offered if the launching process can gather some information about the run, e.g. what process is currently executed, what mojo and if it failed. Also currently features of the build context can only be implemented if running inside the IDE process what has several implications. This now adds a connect API that allows an IDE to supply an extension to the maven process (e.g. via maven.ext.class.path) and set a system property (plexus.build.ipc.port) to communicate with the running maven process. Signed-off-by: Christoph Läubrich --- README.md | 14 +- pom.xml | 6 + .../plexus/build/DefaultBuildContext.java | 14 +- .../plexus/build/connect/BuildConnection.java | 50 ++++ .../plexus/build/connect/Configuration.java | 51 ++++ .../plexus/build/connect/SessionListener.java | 65 ++++ .../build/connect/TcpBuildConnection.java | 281 ++++++++++++++++++ .../build/connect/messages/Message.java | 226 ++++++++++++++ .../connect/messages/ProjectsReadMessage.java | 63 ++++ .../connect/messages/RefreshMessage.java | 47 +++ .../connect/messages/SessionMessage.java | 90 ++++++ .../resources/META-INF/maven/extension.xml | 11 + 12 files changed, 910 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/Configuration.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/SessionListener.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/Message.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/RefreshMessage.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java create mode 100644 src/main/resources/META-INF/maven/extension.xml diff --git a/README.md b/README.md index 6af0dde..1789d9f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ Plexus Build API -======================= +================ [![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/codehaus-plexus/plexus-classworlds.svg?label=License)](http://www.apache.org/licenses/) [![Maven Central](https://img.shields.io/maven-central/v/org.codehaus.plexus/plexus-build-api.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.codehaus.plexus/plexus-build-api) @@ -13,9 +13,8 @@ It supports - fine-grained error/info markers (referring to specific files in particular line numbers) - notifications about updated files - Current Implementations ------ +----------------------- ### Default Implementation @@ -27,6 +26,13 @@ The default implementation shipping with this artifact is supposed to impose min Currently only versions up to 0.0.7 (with old Maven coordinates `org.sonatype.plexus:plexus-build-api`) are supported, this limitation is tracked in [Issue 944](https://github.com/eclipse-m2e/m2e-core/issues/944). History ------ +------- The project was relocated from . Also its Maven coordinates changed from `org.sonatype.plexus:plexus-build-api` to `org.codehaus.plexus:plexus-build-api`, the API is still the same, though. + +## Provided APIs + +### IDE connection to maven process + +This API is usually not used by mojos but for IDE integration, if enabled as a maven-core extension plexus-build-api supply a way to communicate with the running maven build and get events. +The default implementation open a tcp connections to a port specified by the system property `plexus.build.ipc.port` using key/value encoded message format. If no such value is given all messages are silently discarded. diff --git a/pom.xml b/pom.xml index f332246..15a4f4c 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,12 @@ See the Apache License Version 2.0 for the specific language governing permissio + + org.apache.maven + maven-core + 3.9.9 + provided + diff --git a/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java b/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java index 7fb0da6..797079e 100644 --- a/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java +++ b/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java @@ -24,6 +24,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.codehaus.plexus.build.connect.BuildConnection; +import org.codehaus.plexus.build.connect.messages.RefreshMessage; import org.codehaus.plexus.logging.AbstractLogEnabled; import org.codehaus.plexus.util.Scanner; import org.codehaus.plexus.util.io.CachingOutputStream; @@ -56,15 +58,18 @@ public class DefaultBuildContext implements BuildContext { private final Map contextMap = new ConcurrentHashMap<>(); private org.sonatype.plexus.build.incremental.BuildContext legacy; + private BuildConnection connection; /** - * @param legacy the legacy API we delegate to by default, this allow us to - * support "older" plugins and implementors of the API while still - * having a way to move forward! + * @param legacy the legacy API we delegate to by default, this allow us to + * support "older" plugins and implementors of the API while + * still having a way to move forward! + * @param connection the connection we use to forward refresh events */ @Inject - public DefaultBuildContext(org.sonatype.plexus.build.incremental.BuildContext legacy) { + public DefaultBuildContext(org.sonatype.plexus.build.incremental.BuildContext legacy, BuildConnection connection) { this.legacy = legacy; + this.connection = connection; } /** {@inheritDoc} */ @@ -117,6 +122,7 @@ public Scanner newScanner(File basedir) { /** {@inheritDoc} */ public void refresh(File file) { legacy.refresh(file); + connection.send(new RefreshMessage(file.toPath())); } /** {@inheritDoc} */ diff --git a/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java b/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java new file mode 100644 index 0000000..f2b83df --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java @@ -0,0 +1,50 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect; + +import org.codehaus.plexus.build.connect.messages.Message; + +/** + * A {@link BuildConnection} allow communication between a an IDE and a maven + * build to observe the state of the build and act on certain events. This is + * usually not used directly by mojos but invoked internally by other APIs. + */ +public interface BuildConnection { + + /** + * Send a message and returns the reply from the other endpoint, should only be + * called from a maven thread! + * + * @param message the message to send + * @return the reply message or null if this connection is not + * enabled and the message was discarded. + */ + Message send(Message message); + + /** + * This method allows code to perform an eager check if a buildconnection is + * present to send messages. This can be used to guard operations to prevent + * allocate resources or objects if the message will be dropped. + * + * @return true if the connection can be used to send messages or + * if they will be discarded + */ + boolean isEnabled(); + + /** + * Obtains the current configuration, can only be called from a maven thread + * + * @return the active configuration + */ + Configuration getConfiguration(); +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/Configuration.java b/src/main/java/org/codehaus/plexus/build/connect/Configuration.java new file mode 100644 index 0000000..c467b36 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/Configuration.java @@ -0,0 +1,51 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect; + +import org.codehaus.plexus.build.connect.messages.Message; +import org.codehaus.plexus.build.connect.messages.ProjectsReadMessage; + +/** + * Provides access to the configuration provided by the server + */ +public interface Configuration { + + /** + * If this property is set to true in reply to a session start, a + * {@link ProjectsReadMessage} will be send to the endpoint containing all + * projects with their effective model + */ + public static final String CONFIG_SEND_AFTER_PROJECTS_READ = "afterProjectsRead"; + + /** + * @return true if {@link #CONFIG_SEND_AFTER_PROJECTS_READ} is + * provided + */ + public boolean isSendProjects(); + + /** + * Creates a Configuration from a message + * + * @param message + * @return the configuration backed by the message payload + */ + public static Configuration of(Message message) { + return new Configuration() { + + @Override + public boolean isSendProjects() { + return message.getBooleanProperty(CONFIG_SEND_AFTER_PROJECTS_READ, false); + } + }; + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/SessionListener.java b/src/main/java/org/codehaus/plexus/build/connect/SessionListener.java new file mode 100644 index 0000000..c8cea4c --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/SessionListener.java @@ -0,0 +1,65 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.AbstractMavenLifecycleParticipant; +import org.apache.maven.MavenExecutionException; +import org.apache.maven.execution.MavenSession; +import org.codehaus.plexus.build.connect.messages.Message; +import org.codehaus.plexus.build.connect.messages.ProjectsReadMessage; +import org.codehaus.plexus.build.connect.messages.SessionMessage; + +/** + * Listen to session events and send them to the connection + */ +@Named +@Singleton +public class SessionListener extends AbstractMavenLifecycleParticipant { + + @Inject + private BuildConnection connection; + + private boolean sendProjects; + private boolean started; + + @Override + public void afterSessionStart(MavenSession session) throws MavenExecutionException { + started = true; + Message reply = connection.send(new SessionMessage(session, true)); + if (reply != null) { + sendProjects = Configuration.of(reply).isSendProjects(); + } + } + + @Override + public void afterProjectsRead(MavenSession session) throws MavenExecutionException { + if (connection.isEnabled()) { + if (!started) { + afterSessionStart(session); + } + if (sendProjects) { + connection.send(new ProjectsReadMessage(session.getAllProjects())); + } + } + } + + @Override + public void afterSessionEnd(MavenSession session) throws MavenExecutionException { + connection.send(new SessionMessage(session, false)); + started = false; + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java b/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java new file mode 100644 index 0000000..b9b1a95 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java @@ -0,0 +1,281 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.Closeable; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import org.apache.maven.plugin.LegacySupport; +import org.codehaus.plexus.build.connect.messages.Message; +import org.codehaus.plexus.build.connect.messages.SessionMessage; + +/** + * Default implementation using the system property + * plexus.build.ipc.port to communicate with an endpoint to + * exchange messages + */ +@Named("default") +@Singleton +public class TcpBuildConnection implements BuildConnection { + private static final String PLEXUS_BUILD_IPC_PORT = "plexus.build.ipc.port"; + + private static final int PORT = Integer.getInteger(PLEXUS_BUILD_IPC_PORT, 0); + + @Inject + private LegacySupport support; + + private Map configMap = new ConcurrentHashMap<>(); + + private final ThreadLocal connections = + ThreadLocal.withInitial(() -> new TcpClientConnection()); + + @Override + public boolean isEnabled() { + return PORT > 0; + } + + @Override + public Message send(Message message) { + if (isEnabled()) { + String sessionId; + boolean sessionStart; + if (message instanceof SessionMessage) { + sessionId = message.getSessionId(); + sessionStart = ((SessionMessage) message).isSessionStart(); + } else { + sessionId = getThreadSessionId(); + sessionStart = false; + } + byte[] messageBytes = message.serialize(sessionId); + byte[] replyBytes = connections.get().send(messageBytes); + if (replyBytes.length > 0) { + Message reply = Message.decode(replyBytes); + if (reply != null && sessionStart) { + configMap.put(sessionId, Configuration.of(reply)); + } + return reply; + } + } + return null; + } + + private String getThreadSessionId() { + // We must use LegacySupport here to get the currents threads session (what + // might be cloned) + return SessionMessage.getId(support.getSession()); + } + + @Override + public Configuration getConfiguration() { + String id = getThreadSessionId(); + if (id == null) { + throw new IllegalStateException("No session attached to current thread!"); + } + Configuration configuration = configMap.get(id); + if (configuration == null) { + throw new IllegalStateException("No configuration active for session " + id + "!"); + } + return configuration; + } + + /** + * Creates a new server that will receive messages from a remote endpoint and + * inform the consumer + * + * @param consumer the consumer of messages, might be called by different + * threads, if the consumer throws an exception while handling a + * message it will maybe no longer receive some messages. The + * returned map is used as a payload for the reply to the + * server, if null is returned a simple + * acknowledgement without any payload will be send to the + * endpoint. If the consumer performs blocking operations the + * further execution of the maven process might be halted + * depending on the message type, if that is not desired work + * should be offloaded by the consumer to a different thread. + * @return a {@link ServerConnection} that can be used to shutdown the server + * and get properties that needs to be passed to the maven process + * @throws IOException if no local socket can be opened + */ + public static ServerConnection createServer(Function> consumer) throws IOException { + return new ServerConnection(new ServerSocket(0), consumer); + } + + /** + * Represents a server connection that must be created to communicate with the + * maven process using the {@link TcpBuildConnection} + */ + public static final class ServerConnection implements AutoCloseable { + + private ServerSocket socket; + private ExecutorService executor = Executors.newCachedThreadPool(); + private List connections = new ArrayList<>(); + + ServerConnection(ServerSocket socket, Function> consumer) { + this.socket = socket; + executor.execute(() -> { + while (!Thread.currentThread().isInterrupted()) { + try { + TcpServerConnection connection = new TcpServerConnection(socket.accept(), consumer); + connections.add(connection); + executor.execute(connection); + } catch (IOException e) { + return; + } + } + }); + } + + @Override + public void close() { + executor.shutdownNow(); + for (TcpServerConnection connection : connections) { + connection.close(); + } + try { + socket.close(); + } catch (IOException e) { + } + } + + /** + * Given a consumer publishes required properties for a process to launch + * + * @param consumer the consumer for system properties + */ + public void setupProcess(BiConsumer consumer) { + // currently only one but might become more later (e.g. timeout, reconnects, + // ...) + consumer.accept(PLEXUS_BUILD_IPC_PORT, Integer.toString(socket.getLocalPort())); + } + } + + private static final class TcpServerConnection implements Runnable, Closeable { + + private Socket socket; + private Function> consumer; + private DataInputStream in; + private DataOutputStream out; + private AtomicBoolean closed = new AtomicBoolean(); + + public TcpServerConnection(Socket socket, Function> consumer) throws IOException { + this.socket = socket; + this.consumer = consumer; + in = new DataInputStream(socket.getInputStream()); + out = new DataOutputStream(socket.getOutputStream()); + } + + @Override + public void run() { + try { + while (!closed.get() && !Thread.currentThread().isInterrupted()) { + try { + int length = in.readInt(); + if (length == 0) { + return; + } + byte[] bytes = new byte[length]; + in.readFully(bytes); + Message message = Message.decode(bytes); + Map payload = consumer.apply(message); + Message reply = Message.replyTo(message, payload); + byte[] responseBytes = reply.serialize(); + synchronized (out) { + out.writeInt(responseBytes.length); + out.write(responseBytes); + out.flush(); + } + } catch (Exception e) { + return; + } + } + } finally { + close(); + } + } + + @Override + public void close() { + if (closed.compareAndSet(false, true)) { + try { + synchronized (out) { + out.writeInt(0); + out.flush(); + } + } catch (IOException e) { + } + try { + socket.close(); + } catch (IOException e) { + } + } + } + } + + private static final class TcpClientConnection { + + private Socket socket; + private boolean closed; + private DataInputStream in; + private DataOutputStream out; + + public byte[] send(byte[] messageBytes) { + if (!closed) { + try { + if (socket == null) { + socket = new Socket("localhost", PORT); + in = new DataInputStream(socket.getInputStream()); + out = new DataOutputStream(socket.getOutputStream()); + } + out.writeInt(messageBytes.length); + out.write(messageBytes); + out.flush(); + int length = in.readInt(); + if (length == 0) { + socket.close(); + closed = true; + } else { + byte[] bytes = new byte[length]; + in.readFully(bytes); + return bytes; + } + } catch (IOException e) { + closed = true; + if (socket != null) { + try { + socket.close(); + } catch (IOException e1) { + } + } + } + } + return new byte[0]; + } + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java b/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java new file mode 100644 index 0000000..af47e5d --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java @@ -0,0 +1,226 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A message exchanged between two endpoints, usually an IDE and a maven build + */ +public class Message { + private static final ThreadLocal ID = new ThreadLocal() { + private final AtomicLong generator = new AtomicLong(); + + @Override + protected Long initialValue() { + return generator.getAndIncrement(); + } + }; + private final long threadId; + private final Map properties; + private final String sessionId; + + Message(Map payload) { + this(null, ID.get(), payload); + } + + Message(String sessionId, long threadId, Map payload) { + this.sessionId = sessionId; + this.properties = Objects.requireNonNull(payload); + this.threadId = threadId; + } + + /** + * Get a String property from the payload + * + * @param key the key to fetch + * @return the value + */ + public String getProperty(String key) { + return properties.get(key); + } + + /** + * Get a String property from the payload + * + * @param key the key to fetch + * @param defaultValue default value to use when no value is present + * @return the value + */ + public String getProperty(String key, String defaultValue) { + return properties.getOrDefault(key, defaultValue); + } + + /** + * Get a boolean property from the payload + * + * @param key the key to fetch + * @return the value + */ + public boolean getBooleanProperty(String key) { + return Boolean.parseBoolean(properties.get(key)); + } + + /** + * Get a boolean property from the payload + * + * @param key the key to fetch + * @param defaultValue the value to use if not value is present + * @return the value + */ + public boolean getBooleanProperty(String key, boolean defaultValue) { + String property = getProperty(key); + if (property == null) { + return defaultValue; + } + return Boolean.parseBoolean(property); + } + + /** + * @return the remote session id for this message, only valid for messages not + * created locally + */ + public String getSessionId() { + if (sessionId == null) { + throw new IllegalStateException("can not be called on a local message!"); + } + return sessionId; + } + + /** + * @return the bytes using the message session id + */ + public byte[] serialize() { + return serialize(getSessionId()); + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [" + sessionId + "][" + threadId + "] " + properties; + } + + /** + * Creates bytes for this message using the session id + * + * @param sessionId + * @return the bytes using the supplied message id + */ + public byte[] serialize(String sessionId) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(stream); + try { + writeString(sessionId, out); + out.writeLong(threadId); + writeString(getClass().getSimpleName(), out); + if (properties.isEmpty()) { + out.writeInt(0); + } else { + Set> set = properties.entrySet(); + out.writeInt(set.size()); + for (Entry entry : set) { + writeString(entry.getKey(), out); + writeString(entry.getValue(), out); + } + } + } catch (IOException e) { + // should never happen, but if it happens something is wrong! + throw new RuntimeException("Internal Error: Write data failed", e); + } + return stream.toByteArray(); + } + + /** + * Creates a reply to a message using the thread id and session id from the + * original but with the provided payload + * + * @param message the reply message to inherit from + * @param payload the new payload + * @return the message + */ + public static Message replyTo(Message message, Map payload) { + if (payload == null) { + payload = Collections.emptyMap(); + } + return new Message(message.sessionId, message.threadId, payload); + } + + /** + * Decodes a message from its bytes + * + * @param bytes the bytes to decode + * @return the message or null if decoding failed + */ + public static Message decode(byte[] bytes) { + ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + DataInputStream in = new DataInputStream(stream); + try { + String sessionId = readString(in); + long threadId = in.readLong(); + String messageType = readString(in); + int size = in.readInt(); + Map payload = new LinkedHashMap<>(size); + for (int i = 0; i < size; i++) { + payload.put(readString(in), readString(in)); + } + if ("SessionMessage".equals(messageType)) { + return new SessionMessage(sessionId, threadId, payload); + } + if ("ProjectsReadMessage".equals(messageType)) { + return new ProjectsReadMessage(sessionId, threadId, payload); + } + if ("RefreshMessage".equals(messageType)) { + return new RefreshMessage(sessionId, threadId, payload); + } + return new Message(sessionId, threadId, payload); + } catch (IOException e) { + // should never happen, but if it happens something is wrong! + System.err.println("Internal Error: Message decoding failed: " + e); + } + return null; + } + + private static String readString(DataInputStream in) throws IOException { + int length = in.readInt(); + if (length < 0) { + return null; + } + if (length == 0) { + return ""; + } + byte[] bs = new byte[length]; + in.readFully(bs); + return new String(bs, StandardCharsets.UTF_8); + } + + private static void writeString(String string, DataOutputStream stream) throws IOException { + if (string == null) { + stream.writeInt(-1); + } else { + byte[] bytes = string.getBytes(StandardCharsets.UTF_8); + stream.writeInt(bytes.length); + stream.write(bytes); + } + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java new file mode 100644 index 0000000..91825ef --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java @@ -0,0 +1,63 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.DefaultModelWriter; +import org.apache.maven.project.MavenProject; + +/** + * Message send to inform about reactor project in the build and their effective + * model + */ +public class ProjectsReadMessage extends Message { + + private static final DefaultModelWriter MODEL_WRITER = new DefaultModelWriter(); + + ProjectsReadMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } + + /** + * @param projects the projects to send + */ + public ProjectsReadMessage(Collection projects) { + super(buildMap(projects)); + } + + private static Map buildMap(Collection projects) { + Map map = new HashMap<>(); + for (MavenProject project : projects) { + String key = project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(); + map.put(key, getEffectiveModel(project)); + } + return map; + } + + private static String getEffectiveModel(MavenProject project) { + Model model = project.getModel(); + StringWriter writer = new StringWriter(); + try { + MODEL_WRITER.write(writer, null, model); + } catch (IOException e) { + } + String string = writer.toString(); + return string; + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/RefreshMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/RefreshMessage.java new file mode 100644 index 0000000..a473a02 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/RefreshMessage.java @@ -0,0 +1,47 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.io.File; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; + +/** + * A message that indicates a path should be refreshed (e.g. because new files + * are placed in a generated folder) + */ +public class RefreshMessage extends Message { + + private static final String PATH_KEY = "path"; + + /** + * Create a new message to refresh a path + * + * @param path the path to refresh + */ + public RefreshMessage(Path path) { + super(Collections.singletonMap(PATH_KEY, path.toFile().getAbsolutePath())); + } + + /** + * @return the path to refresh + */ + public Path getPath() { + return new File(getProperty(PATH_KEY)).toPath(); + } + + RefreshMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java new file mode 100644 index 0000000..f88d906 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java @@ -0,0 +1,90 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; + +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; + +/** + * Event that is received / send when a session starts/end + */ +public class SessionMessage extends Message { + + private static final String SESSION_EXECUTION_ROOT_DIRECTORY = "sessionExecutionRootDirectory"; + private static final String SESSION_START = "sessionStart"; + private static final String SESSION_ID = "sessionId"; + private static final Map ID_MAP = new WeakHashMap<>(); + + /** + * Creates a new session message + * + * @param session the session to use + * @param start true if it is a start of the session or + * false if it is the end of a session + */ + public SessionMessage(MavenSession session, boolean start) { + super(buildMap(session, start)); + } + + SessionMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } + + public String getSessionId() { + return getProperty(SESSION_ID); + } + + /** + * @return true if this is a session start event + */ + public boolean isSessionStart() { + return getBooleanProperty(SESSION_START); + } + + /** + * @return the value of the ExecutionRootDirectory of this session + */ + public String getExecutionRootDirectory() { + return getProperty(SESSION_EXECUTION_ROOT_DIRECTORY); + } + + /** + * Returns the unique ID for a session + * + * @param session the session to get an Id for + * @return the id of the session or the name of the current thread if the + * session is null + */ + public static synchronized String getId(MavenSession session) { + if (session == null) { + return Thread.currentThread().getName(); + } + // we can't use the session itself as a key, because sessions might be cloned, + // but the execution request should (hopefully) stay constant... + return ID_MAP.computeIfAbsent( + session.getRequest(), x -> UUID.randomUUID().toString()); + } + + private static Map buildMap(MavenSession session, boolean start) { + Map map = new HashMap<>(2); + map.put(SESSION_ID, getId(session)); + map.put(SESSION_START, Boolean.toString(start)); + map.put(SESSION_EXECUTION_ROOT_DIRECTORY, session.getExecutionRootDirectory()); + return map; + } +} diff --git a/src/main/resources/META-INF/maven/extension.xml b/src/main/resources/META-INF/maven/extension.xml new file mode 100644 index 0000000..181a211 --- /dev/null +++ b/src/main/resources/META-INF/maven/extension.xml @@ -0,0 +1,11 @@ + + + + + org.codehaus.plexus.build + + + + org.codehaus.plexus:plexus-build-api + + From 490f7017a924bef847a4de54c168318d6e810035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 22 Feb 2025 15:30:30 +0100 Subject: [PATCH 16/22] Use EventSpy to handle execution of events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we use an AbstractMavenLifecycleParticipant but this only offers limited number of events, an EventSpy is much more flexible and offers much more insights. This now replaces the SessionListener with an EventSpy and also adds support for project execution events. Alongside with that the configuration is now local to the EventListener and no longer part of the connection implementation. Signed-off-by: Christoph Läubrich --- .../plexus/build/DefaultBuildContext.java | 19 ++- .../plexus/build/connect/BuildConnection.java | 13 +- .../plexus/build/connect/Configuration.java | 11 +- .../plexus/build/connect/EventListener.java | 100 ++++++++++++ .../plexus/build/connect/SessionListener.java | 65 -------- .../build/connect/TcpBuildConnection.java | 50 ++---- .../build/connect/messages/InitMessage.java | 34 +++++ .../build/connect/messages/Message.java | 18 ++- .../connect/messages/ProjectMessage.java | 122 +++++++++++++++ .../connect/messages/ProjectsMessage.java | 143 ++++++++++++++++++ .../connect/messages/ProjectsReadMessage.java | 63 -------- .../connect/messages/SessionMessage.java | 27 ---- 12 files changed, 447 insertions(+), 218 deletions(-) create mode 100644 src/main/java/org/codehaus/plexus/build/connect/EventListener.java delete mode 100644 src/main/java/org/codehaus/plexus/build/connect/SessionListener.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsMessage.java delete mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java diff --git a/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java b/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java index 797079e..2b34e71 100644 --- a/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java +++ b/src/main/java/org/codehaus/plexus/build/DefaultBuildContext.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.maven.plugin.LegacySupport; import org.codehaus.plexus.build.connect.BuildConnection; import org.codehaus.plexus.build.connect.messages.RefreshMessage; import org.codehaus.plexus.logging.AbstractLogEnabled; @@ -59,17 +60,23 @@ public class DefaultBuildContext implements BuildContext { private final Map contextMap = new ConcurrentHashMap<>(); private org.sonatype.plexus.build.incremental.BuildContext legacy; private BuildConnection connection; + private LegacySupport legacySupport; /** - * @param legacy the legacy API we delegate to by default, this allow us to - * support "older" plugins and implementors of the API while - * still having a way to move forward! - * @param connection the connection we use to forward refresh events + * @param legacy the legacy API we delegate to by default, this allow us + * to support "older" plugins and implementors of the API + * while still having a way to move forward! + * @param connection the connection we use to forward refresh events + * @param legacySupport legacy support to get the current session */ @Inject - public DefaultBuildContext(org.sonatype.plexus.build.incremental.BuildContext legacy, BuildConnection connection) { + public DefaultBuildContext( + org.sonatype.plexus.build.incremental.BuildContext legacy, + BuildConnection connection, + LegacySupport legacySupport) { this.legacy = legacy; this.connection = connection; + this.legacySupport = legacySupport; } /** {@inheritDoc} */ @@ -122,7 +129,7 @@ public Scanner newScanner(File basedir) { /** {@inheritDoc} */ public void refresh(File file) { legacy.refresh(file); - connection.send(new RefreshMessage(file.toPath())); + connection.send(new RefreshMessage(file.toPath()), legacySupport.getSession()); } /** {@inheritDoc} */ diff --git a/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java b/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java index f2b83df..921115e 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java +++ b/src/main/java/org/codehaus/plexus/build/connect/BuildConnection.java @@ -12,6 +12,7 @@ */ package org.codehaus.plexus.build.connect; +import org.apache.maven.execution.MavenSession; import org.codehaus.plexus.build.connect.messages.Message; /** @@ -25,11 +26,12 @@ public interface BuildConnection { * Send a message and returns the reply from the other endpoint, should only be * called from a maven thread! * - * @param message the message to send + * @param message the message to send + * @param mavenSession the maven session to reference * @return the reply message or null if this connection is not * enabled and the message was discarded. */ - Message send(Message message); + Message send(Message message, MavenSession mavenSession); /** * This method allows code to perform an eager check if a buildconnection is @@ -40,11 +42,4 @@ public interface BuildConnection { * if they will be discarded */ boolean isEnabled(); - - /** - * Obtains the current configuration, can only be called from a maven thread - * - * @return the active configuration - */ - Configuration getConfiguration(); } diff --git a/src/main/java/org/codehaus/plexus/build/connect/Configuration.java b/src/main/java/org/codehaus/plexus/build/connect/Configuration.java index c467b36..2444145 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/Configuration.java +++ b/src/main/java/org/codehaus/plexus/build/connect/Configuration.java @@ -13,7 +13,6 @@ package org.codehaus.plexus.build.connect; import org.codehaus.plexus.build.connect.messages.Message; -import org.codehaus.plexus.build.connect.messages.ProjectsReadMessage; /** * Provides access to the configuration provided by the server @@ -21,14 +20,12 @@ public interface Configuration { /** - * If this property is set to true in reply to a session start, a - * {@link ProjectsReadMessage} will be send to the endpoint containing all - * projects with their effective model + * If this property is set to true in reply to a InitMessage */ - public static final String CONFIG_SEND_AFTER_PROJECTS_READ = "afterProjectsRead"; + public static final String CONFIG_SEND_PROJECTS = "sendProjectInfos"; /** - * @return true if {@link #CONFIG_SEND_AFTER_PROJECTS_READ} is + * @return true if {@link #CONFIG_SEND_PROJECTS} is * provided */ public boolean isSendProjects(); @@ -44,7 +41,7 @@ public static Configuration of(Message message) { @Override public boolean isSendProjects() { - return message.getBooleanProperty(CONFIG_SEND_AFTER_PROJECTS_READ, false); + return message.getBooleanProperty(CONFIG_SEND_PROJECTS, false); } }; } diff --git a/src/main/java/org/codehaus/plexus/build/connect/EventListener.java b/src/main/java/org/codehaus/plexus/build/connect/EventListener.java new file mode 100644 index 0000000..5806c08 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/EventListener.java @@ -0,0 +1,100 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.eventspy.EventSpy; +import org.apache.maven.execution.ExecutionEvent; +import org.apache.maven.execution.ExecutionEvent.Type; +import org.apache.maven.execution.MavenSession; +import org.codehaus.plexus.build.connect.messages.InitMessage; +import org.codehaus.plexus.build.connect.messages.Message; +import org.codehaus.plexus.build.connect.messages.ProjectMessage; +import org.codehaus.plexus.build.connect.messages.ProjectsMessage; +import org.codehaus.plexus.build.connect.messages.SessionMessage; + +/** + * Listen to all maven events and forward them to the endpoint + */ +@Named +@Singleton +public class EventListener implements EventSpy { + + private BuildConnection connection; + private Configuration configuration; + + /** + * Creates endpoint for the given connection + * + * @param connection injected + */ + @Inject + public EventListener(BuildConnection connection) { + this.connection = connection; + } + + @Override + public void init(Context context) throws Exception { + Map data = new LinkedHashMap<>(); + context.getData().forEach((k, v) -> { + data.put(k, String.valueOf(v)); + }); + Message message = connection.send(new InitMessage(data), null); + if (message != null) { + configuration = Configuration.of(message); + } + } + + @Override + public void onEvent(Object event) throws Exception { + if (configuration == null) { + return; + } + if (event instanceof ExecutionEvent) { + handleExecutionEvent((ExecutionEvent) event); + } + } + + private void handleExecutionEvent(ExecutionEvent executionEvent) { + MavenSession session = executionEvent.getSession(); + Type type = executionEvent.getType(); + switch (type) { + case SessionStarted: + connection.send(new SessionMessage(session, true), session); + if (configuration.isSendProjects()) { + connection.send(new ProjectsMessage(session.getProjects()), session); + } + break; + case SessionEnded: + connection.send(new SessionMessage(session, false), session); + break; + case ProjectStarted: + case ProjectFailed: + case ProjectSkipped: + case ProjectSucceeded: + connection.send(new ProjectMessage(executionEvent.getProject(), type), session); + break; + default: + break; + } + } + + @Override + public void close() throws Exception {} +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/SessionListener.java b/src/main/java/org/codehaus/plexus/build/connect/SessionListener.java deleted file mode 100644 index c8cea4c..0000000 --- a/src/main/java/org/codehaus/plexus/build/connect/SessionListener.java +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright (c) 2025 Christoph Läubrich All rights reserved. - -This program is licensed to you under the Apache License Version 2.0, -and you may not use this file except in compliance with the Apache License Version 2.0. -You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. - -Unless required by applicable law or agreed to in writing, -software distributed under the Apache License Version 2.0 is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. -*/ -package org.codehaus.plexus.build.connect; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.apache.maven.AbstractMavenLifecycleParticipant; -import org.apache.maven.MavenExecutionException; -import org.apache.maven.execution.MavenSession; -import org.codehaus.plexus.build.connect.messages.Message; -import org.codehaus.plexus.build.connect.messages.ProjectsReadMessage; -import org.codehaus.plexus.build.connect.messages.SessionMessage; - -/** - * Listen to session events and send them to the connection - */ -@Named -@Singleton -public class SessionListener extends AbstractMavenLifecycleParticipant { - - @Inject - private BuildConnection connection; - - private boolean sendProjects; - private boolean started; - - @Override - public void afterSessionStart(MavenSession session) throws MavenExecutionException { - started = true; - Message reply = connection.send(new SessionMessage(session, true)); - if (reply != null) { - sendProjects = Configuration.of(reply).isSendProjects(); - } - } - - @Override - public void afterProjectsRead(MavenSession session) throws MavenExecutionException { - if (connection.isEnabled()) { - if (!started) { - afterSessionStart(session); - } - if (sendProjects) { - connection.send(new ProjectsReadMessage(session.getAllProjects())); - } - } - } - - @Override - public void afterSessionEnd(MavenSession session) throws MavenExecutionException { - connection.send(new SessionMessage(session, false)); - started = false; - } -} diff --git a/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java b/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java index b9b1a95..339487e 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java +++ b/src/main/java/org/codehaus/plexus/build/connect/TcpBuildConnection.java @@ -12,7 +12,6 @@ */ package org.codehaus.plexus.build.connect; -import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -25,16 +24,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; import java.util.function.Function; -import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.execution.MavenSession; import org.codehaus.plexus.build.connect.messages.Message; -import org.codehaus.plexus.build.connect.messages.SessionMessage; /** * Default implementation using the system property @@ -48,10 +47,7 @@ public class TcpBuildConnection implements BuildConnection { private static final int PORT = Integer.getInteger(PLEXUS_BUILD_IPC_PORT, 0); - @Inject - private LegacySupport support; - - private Map configMap = new ConcurrentHashMap<>(); + private final Map sessionMap = new WeakHashMap<>(); private final ThreadLocal connections = ThreadLocal.withInitial(() -> new TcpClientConnection()); @@ -62,47 +58,23 @@ public boolean isEnabled() { } @Override - public Message send(Message message) { + public Message send(Message message, MavenSession mavenSession) { if (isEnabled()) { - String sessionId; - boolean sessionStart; - if (message instanceof SessionMessage) { - sessionId = message.getSessionId(); - sessionStart = ((SessionMessage) message).isSessionStart(); - } else { - sessionId = getThreadSessionId(); - sessionStart = false; - } + String sessionId = getId(mavenSession); byte[] messageBytes = message.serialize(sessionId); byte[] replyBytes = connections.get().send(messageBytes); if (replyBytes.length > 0) { - Message reply = Message.decode(replyBytes); - if (reply != null && sessionStart) { - configMap.put(sessionId, Configuration.of(reply)); - } - return reply; + return Message.decode(replyBytes); } } return null; } - private String getThreadSessionId() { - // We must use LegacySupport here to get the currents threads session (what - // might be cloned) - return SessionMessage.getId(support.getSession()); - } - - @Override - public Configuration getConfiguration() { - String id = getThreadSessionId(); - if (id == null) { - throw new IllegalStateException("No session attached to current thread!"); - } - Configuration configuration = configMap.get(id); - if (configuration == null) { - throw new IllegalStateException("No configuration active for session " + id + "!"); + private synchronized String getId(MavenSession session) { + if (session == null) { + return Thread.currentThread().getName(); } - return configuration; + return sessionMap.computeIfAbsent(session, x -> UUID.randomUUID().toString()); } /** diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java new file mode 100644 index 0000000..7239b92 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java @@ -0,0 +1,34 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.util.Map; + +/** + * Message send to init the inital communication with the endpoints + */ +public class InitMessage extends Message { + + /** + * Creates a message with inital information about the running maven system + * + * @param settings the context settings to send to the endpoint + */ + public InitMessage(Map settings) { + super(settings); + } + + InitMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java b/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java index af47e5d..4400f22 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; /** * A message exchanged between two endpoints, usually an IDE and a maven build @@ -52,6 +53,13 @@ protected Long initialValue() { this.threadId = threadId; } + /** + * @return the keys stored in this message + */ + public Stream keys() { + return properties.keySet().stream(); + } + /** * Get a String property from the payload * @@ -187,12 +195,18 @@ public static Message decode(byte[] bytes) { if ("SessionMessage".equals(messageType)) { return new SessionMessage(sessionId, threadId, payload); } - if ("ProjectsReadMessage".equals(messageType)) { - return new ProjectsReadMessage(sessionId, threadId, payload); + if ("ProjectsMessage".equals(messageType)) { + return new ProjectsMessage(sessionId, threadId, payload); } if ("RefreshMessage".equals(messageType)) { return new RefreshMessage(sessionId, threadId, payload); } + if ("InitMessage".equals(messageType)) { + return new InitMessage(sessionId, threadId, payload); + } + if ("ProjectMessage".equals(messageType)) { + return new ProjectMessage(sessionId, threadId, payload); + } return new Message(sessionId, threadId, payload); } catch (IOException e) { // should never happen, but if it happens something is wrong! diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java new file mode 100644 index 0000000..68a30fd --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java @@ -0,0 +1,122 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.io.File; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.execution.ExecutionEvent.Type; +import org.apache.maven.project.MavenProject; + +/** + * Send to inform about project changes + */ +public class ProjectMessage extends Message { + + private static final String BASE_DIR = "baseDir"; + private static final String VERSION = "version"; + private static final String ARTIFACT_ID = "artifactId"; + private static final String GROUP_ID = "groupId"; + private static final String EVENT_TYPE = "eventType"; + + ProjectMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } + + /** + * Constructs a new event based on project and type + * + * @param mavenProject + * @param eventtype + */ + public ProjectMessage(MavenProject mavenProject, Type eventtype) { + super(toMap(mavenProject, eventtype)); + } + + /** + * @return the group id of the project + */ + public String getGroupId() { + return getProperty(GROUP_ID); + } + + /** + * @return the artifact id of the project + */ + public String getArtifactId() { + return getProperty(ARTIFACT_ID); + } + + /** + * @return the version of the project + */ + public String getVersion() { + return getProperty(ARTIFACT_ID); + } + + /** + * @return the basedir of the project + */ + public Path getBaseDir() { + return new File(getProperty(BASE_DIR)).toPath(); + } + + /** + * @return the type of the event + */ + public EventType getType() { + try { + return EventType.valueOf(getProperty(EVENT_TYPE)); + } catch (RuntimeException e) { + return EventType.Unknown; + } + } + + private static Map toMap(MavenProject mavenProject, Type eventtype) { + Map map = new HashMap<>(); + map.put(EVENT_TYPE, eventtype.name()); + map.put(GROUP_ID, mavenProject.getGroupId()); + map.put(ARTIFACT_ID, mavenProject.getArtifactId()); + map.put(VERSION, mavenProject.getVersion()); + map.put(BASE_DIR, mavenProject.getBasedir().getAbsolutePath()); + return map; + } + + /** + * Describe the type of the event + */ + public enum EventType { + /** + * The project was started + */ + ProjectStarted, + /** + * The project failed + */ + ProjectFailed, + /** + * The project was skipped + */ + ProjectSkipped, + /** + * the project succeed + */ + ProjectSucceeded, + /** + * the type of event is unknown + */ + Unknown; + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsMessage.java new file mode 100644 index 0000000..93bfa5a --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsMessage.java @@ -0,0 +1,143 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.DefaultModelWriter; +import org.apache.maven.project.MavenProject; + +/** + * Message send to inform about reactor project in the build and their effective + * model + */ +public class ProjectsMessage extends Message { + + private static final DefaultModelWriter MODEL_WRITER = new DefaultModelWriter(); + + ProjectsMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } + + /** + * @param projects the projects to send + */ + public ProjectsMessage(Collection projects) { + super(buildMap(projects)); + } + + /** + * @return a stream of project infos + */ + public Stream projects() { + return keys().map(key -> { + String[] gav = key.split("\t"); + if (gav.length == 5 && "p".equals(gav[0])) { + ProjectInfo pi = new ProjectInfo() { + + @Override + public String getGroupId() { + return gav[1]; + } + + @Override + public String getArtifactId() { + return gav[2]; + } + + @Override + public String getVersion() { + return gav[3]; + } + + @Override + public String getModel() { + return getProperty(key); + } + + @Override + public Path getBaseDir() { + return new File(gav[4]).toPath(); + } + }; + return pi; + } + return null; + }) + .filter(Objects::nonNull); + } + + private static Map buildMap(Collection projects) { + Map map = new HashMap<>(); + for (MavenProject project : projects) { + String key = String.format( + "p\t%s\t%s\t%s\t%s", + project.getGroupId(), + project.getArtifactId(), + project.getVersion(), + project.getBasedir().getAbsolutePath()); + map.put(key, getEffectiveModel(project)); + } + return map; + } + + private static String getEffectiveModel(MavenProject project) { + Model model = project.getModel(); + StringWriter writer = new StringWriter(); + try { + MODEL_WRITER.write(writer, null, model); + } catch (IOException e) { + } + String string = writer.toString(); + return string; + } + + /** + * Holds basic project info + */ + public static interface ProjectInfo { + /** + * @return the group id of the reactor project + */ + String getGroupId(); + + /** + * @return the artifact id of the reactor project + */ + String getArtifactId(); + + /** + * @return the version of the reactor project + */ + String getVersion(); + + /** + * @return the effective model of the project + */ + String getModel(); + + /** + * @return the basedir of the project + */ + Path getBaseDir(); + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java deleted file mode 100644 index 91825ef..0000000 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectsReadMessage.java +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright (c) 2025 Christoph Läubrich All rights reserved. - -This program is licensed to you under the Apache License Version 2.0, -and you may not use this file except in compliance with the Apache License Version 2.0. -You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. - -Unless required by applicable law or agreed to in writing, -software distributed under the Apache License Version 2.0 is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. -*/ -package org.codehaus.plexus.build.connect.messages; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import org.apache.maven.model.Model; -import org.apache.maven.model.io.DefaultModelWriter; -import org.apache.maven.project.MavenProject; - -/** - * Message send to inform about reactor project in the build and their effective - * model - */ -public class ProjectsReadMessage extends Message { - - private static final DefaultModelWriter MODEL_WRITER = new DefaultModelWriter(); - - ProjectsReadMessage(String sessionId, long threadId, Map payload) { - super(sessionId, threadId, payload); - } - - /** - * @param projects the projects to send - */ - public ProjectsReadMessage(Collection projects) { - super(buildMap(projects)); - } - - private static Map buildMap(Collection projects) { - Map map = new HashMap<>(); - for (MavenProject project : projects) { - String key = project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(); - map.put(key, getEffectiveModel(project)); - } - return map; - } - - private static String getEffectiveModel(MavenProject project) { - Model model = project.getModel(); - StringWriter writer = new StringWriter(); - try { - MODEL_WRITER.write(writer, null, model); - } catch (IOException e) { - } - String string = writer.toString(); - return string; - } -} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java index f88d906..2f9e0a6 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/SessionMessage.java @@ -14,10 +14,7 @@ import java.util.HashMap; import java.util.Map; -import java.util.UUID; -import java.util.WeakHashMap; -import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; /** @@ -27,8 +24,6 @@ public class SessionMessage extends Message { private static final String SESSION_EXECUTION_ROOT_DIRECTORY = "sessionExecutionRootDirectory"; private static final String SESSION_START = "sessionStart"; - private static final String SESSION_ID = "sessionId"; - private static final Map ID_MAP = new WeakHashMap<>(); /** * Creates a new session message @@ -45,10 +40,6 @@ public SessionMessage(MavenSession session, boolean start) { super(sessionId, threadId, payload); } - public String getSessionId() { - return getProperty(SESSION_ID); - } - /** * @return true if this is a session start event */ @@ -63,26 +54,8 @@ public String getExecutionRootDirectory() { return getProperty(SESSION_EXECUTION_ROOT_DIRECTORY); } - /** - * Returns the unique ID for a session - * - * @param session the session to get an Id for - * @return the id of the session or the name of the current thread if the - * session is null - */ - public static synchronized String getId(MavenSession session) { - if (session == null) { - return Thread.currentThread().getName(); - } - // we can't use the session itself as a key, because sessions might be cloned, - // but the execution request should (hopefully) stay constant... - return ID_MAP.computeIfAbsent( - session.getRequest(), x -> UUID.randomUUID().toString()); - } - private static Map buildMap(MavenSession session, boolean start) { Map map = new HashMap<>(2); - map.put(SESSION_ID, getId(session)); map.put(SESSION_START, Boolean.toString(start)); map.put(SESSION_EXECUTION_ROOT_DIRECTORY, session.getExecutionRootDirectory()); return map; From 65e754432286f8cd6394868647e22f49eb7bd2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 22 Feb 2025 17:04:51 +0100 Subject: [PATCH 17/22] Add support for sending mojo execution events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christoph Läubrich --- .../plexus/build/connect/EventListener.java | 24 +-- .../build/connect/messages/InitMessage.java | 26 +++- .../build/connect/messages/Message.java | 25 ++++ .../build/connect/messages/MojoMessage.java | 138 ++++++++++++++++++ .../connect/messages/ProjectMessage.java | 2 +- 5 files changed, 199 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/codehaus/plexus/build/connect/messages/MojoMessage.java diff --git a/src/main/java/org/codehaus/plexus/build/connect/EventListener.java b/src/main/java/org/codehaus/plexus/build/connect/EventListener.java index 5806c08..1eb5e8d 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/EventListener.java +++ b/src/main/java/org/codehaus/plexus/build/connect/EventListener.java @@ -12,9 +12,6 @@ */ package org.codehaus.plexus.build.connect; -import java.util.LinkedHashMap; -import java.util.Map; - import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -25,6 +22,7 @@ import org.apache.maven.execution.MavenSession; import org.codehaus.plexus.build.connect.messages.InitMessage; import org.codehaus.plexus.build.connect.messages.Message; +import org.codehaus.plexus.build.connect.messages.MojoMessage; import org.codehaus.plexus.build.connect.messages.ProjectMessage; import org.codehaus.plexus.build.connect.messages.ProjectsMessage; import org.codehaus.plexus.build.connect.messages.SessionMessage; @@ -51,11 +49,7 @@ public EventListener(BuildConnection connection) { @Override public void init(Context context) throws Exception { - Map data = new LinkedHashMap<>(); - context.getData().forEach((k, v) -> { - data.put(k, String.valueOf(v)); - }); - Message message = connection.send(new InitMessage(data), null); + Message message = connection.send(new InitMessage(context), null); if (message != null) { configuration = Configuration.of(message); } @@ -71,9 +65,9 @@ public void onEvent(Object event) throws Exception { } } - private void handleExecutionEvent(ExecutionEvent executionEvent) { - MavenSession session = executionEvent.getSession(); - Type type = executionEvent.getType(); + private void handleExecutionEvent(ExecutionEvent event) { + MavenSession session = event.getSession(); + Type type = event.getType(); switch (type) { case SessionStarted: connection.send(new SessionMessage(session, true), session); @@ -88,7 +82,13 @@ private void handleExecutionEvent(ExecutionEvent executionEvent) { case ProjectFailed: case ProjectSkipped: case ProjectSucceeded: - connection.send(new ProjectMessage(executionEvent.getProject(), type), session); + connection.send(new ProjectMessage(event.getProject(), type), session); + break; + case MojoStarted: + case MojoFailed: + case MojoSkipped: + case MojoSucceeded: + connection.send(new MojoMessage(event.getMojoExecution(), type), session); break; default: break; diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java index 7239b92..e8cabcb 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/InitMessage.java @@ -12,7 +12,11 @@ */ package org.codehaus.plexus.build.connect.messages; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Properties; + +import org.apache.maven.eventspy.EventSpy.Context; /** * Message send to init the inital communication with the endpoints @@ -22,10 +26,26 @@ public class InitMessage extends Message { /** * Creates a message with inital information about the running maven system * - * @param settings the context settings to send to the endpoint + * @param context the context settings to send to the endpoint */ - public InitMessage(Map settings) { - super(settings); + public InitMessage(Context context) { + super(toMap(context)); + } + + private static Map toMap(Context context) { + Map data = new LinkedHashMap<>(); + context.getData().forEach((k, v) -> { + if (v instanceof String) { + data.put(k, (String) v); + } + if (v instanceof Properties) { + Properties properties = (Properties) v; + for (String p : properties.stringPropertyNames()) { + data.put(k + "." + p, properties.getProperty(p)); + } + } + }); + return data; } InitMessage(String sessionId, long threadId, Map payload) { diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java b/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java index 4400f22..ddc1430 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/Message.java @@ -207,6 +207,9 @@ public static Message decode(byte[] bytes) { if ("ProjectMessage".equals(messageType)) { return new ProjectMessage(sessionId, threadId, payload); } + if ("MojoMessage".equals(messageType)) { + return new MojoMessage(sessionId, threadId, payload); + } return new Message(sessionId, threadId, payload); } catch (IOException e) { // should never happen, but if it happens something is wrong! @@ -237,4 +240,26 @@ private static void writeString(String string, DataOutputStream stream) throws I stream.write(bytes); } } + + @Override + public int hashCode() { + return Objects.hash(properties, sessionId, threadId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Message other = (Message) obj; + return Objects.equals(properties, other.properties) + && Objects.equals(sessionId, other.sessionId) + && threadId == other.threadId; + } } diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/MojoMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/MojoMessage.java new file mode 100644 index 0000000..e1beb34 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/MojoMessage.java @@ -0,0 +1,138 @@ +/* +Copyright (c) 2025 Christoph Läubrich All rights reserved. + +This program is licensed to you under the Apache License Version 2.0, +and you may not use this file except in compliance with the Apache License Version 2.0. +You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, +software distributed under the Apache License Version 2.0 is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. +*/ +package org.codehaus.plexus.build.connect.messages; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.maven.execution.ExecutionEvent.Type; +import org.apache.maven.plugin.MojoExecution; + +/** + * Mesaage generated when a mojo is executed + */ +public class MojoMessage extends Message { + + private static final String GOAL = "goal"; + private static final String LIFECYCLE_PHASE = "lifecyclePhase"; + private static final String EXECUTION_ID = "executionId"; + private static final String VERSION = "version"; + private static final String ARTIFACT_ID = "artifactId"; + private static final String GROUP_ID = "groupId"; + private static final String EVENT_TYPE = "eventType"; + + MojoMessage(String sessionId, long threadId, Map payload) { + super(sessionId, threadId, payload); + } + + /** + * Creates a Mojo message from execution and type + * + * @param mojoExecution + * @param type + */ + public MojoMessage(MojoExecution mojoExecution, Type type) { + super(toMap(mojoExecution, type)); + } + + /** + * @return the group id + */ + public String getGroupId() { + return getProperty(GROUP_ID); + } + + /** + * @return the artifact id + */ + public String getArtifactId() { + return getProperty(ARTIFACT_ID); + } + + /** + * @return the version + */ + public String getVersion() { + return getProperty(VERSION); + } + + /** + * @return the execution id + */ + public String getExecutionId() { + return getProperty(EXECUTION_ID); + } + + /** + * @return the lifecycle phase + */ + public String getLifecyclePhase() { + return getProperty(LIFECYCLE_PHASE); + } + + /** + * @return the lifecycle phase + */ + public String getGoal() { + return getProperty(GOAL); + } + + /** + * @return the type of event + */ + public EventType getType() { + try { + return EventType.valueOf(getProperty(EVENT_TYPE)); + } catch (RuntimeException e) { + return EventType.Unknown; + } + } + + private static Map toMap(MojoExecution mojoExecution, Type type) { + Map map = new HashMap<>(); + map.put(EVENT_TYPE, type.name()); + map.put(GROUP_ID, mojoExecution.getGroupId()); + map.put(ARTIFACT_ID, mojoExecution.getArtifactId()); + map.put(VERSION, mojoExecution.getVersion()); + map.put(EXECUTION_ID, mojoExecution.getExecutionId()); + map.put(LIFECYCLE_PHASE, mojoExecution.getLifecyclePhase()); + map.put(GOAL, mojoExecution.getGoal()); + return map; + } + + /** + * create the event type + */ + public static enum EventType { + /** + * the mojo was started + */ + MojoStarted, + /** + * The mojo failed + */ + MojoFailed, + /** + * The mojo was skipped + */ + MojoSkipped, + /** + * The mojo succeed + */ + MojoSucceeded, + /** + * the type is unknown + */ + Unknown; + } +} diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java index 68a30fd..3180263 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java @@ -97,7 +97,7 @@ private static Map toMap(MavenProject mavenProject, Type eventty /** * Describe the type of the event */ - public enum EventType { + public static enum EventType { /** * The project was started */ From 5ed541d86bb7d7ca6ddb997d8c4524d20a35fbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 22 Feb 2025 17:47:09 +0100 Subject: [PATCH 18/22] Fix Artifact id returned instead of project version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christoph Läubrich --- .../codehaus/plexus/build/connect/messages/ProjectMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java index 3180263..0ec2ef1 100644 --- a/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java +++ b/src/main/java/org/codehaus/plexus/build/connect/messages/ProjectMessage.java @@ -63,7 +63,7 @@ public String getArtifactId() { * @return the version of the project */ public String getVersion() { - return getProperty(ARTIFACT_ID); + return getProperty(VERSION); } /** From bd53adab977f51b13f50eb98d7f6e54aa60b9e59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 02:29:07 +0000 Subject: [PATCH 19/22] Bump org.codehaus.plexus:plexus from 20 to 21 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 20 to 21. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-version: '21' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 15a4f4c..92e9222 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 20 + 21 plexus-build-api From 9780eec14d02a8c23f9aa0d79039adf542906d9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 02:21:28 +0000 Subject: [PATCH 20/22] Bump org.eclipse.sisu:org.eclipse.sisu.plexus from 0.9.0.M3 to 0.9.0.M4 Bumps [org.eclipse.sisu:org.eclipse.sisu.plexus](https://github.com/eclipse-sisu/sisu-project) from 0.9.0.M3 to 0.9.0.M4. - [Release notes](https://github.com/eclipse-sisu/sisu-project/releases) - [Changelog](https://github.com/eclipse-sisu/sisu-project/blob/main/RELEASE.md) - [Commits](https://github.com/eclipse-sisu/sisu-project/compare/milestones/0.9.0.M3...milestones/0.9.0.M4) --- updated-dependencies: - dependency-name: org.eclipse.sisu:org.eclipse.sisu.plexus dependency-version: 0.9.0.M4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 92e9222..d866d5d 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.eclipse.sisu org.eclipse.sisu.plexus - 0.9.0.M3 + 0.9.0.M4 * From 4615b9b75f12326193a1717559f545252f06181a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 02:28:29 +0000 Subject: [PATCH 21/22] Bump org.codehaus.plexus:plexus from 21 to 22 Bumps [org.codehaus.plexus:plexus](https://github.com/codehaus-plexus/plexus-pom) from 21 to 22. - [Release notes](https://github.com/codehaus-plexus/plexus-pom/releases) - [Commits](https://github.com/codehaus-plexus/plexus-pom/commits) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus dependency-version: '22' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d866d5d..26d9825 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.codehaus.plexus plexus - 21 + 22 plexus-build-api From 1367b498a5bf1a7b98de5cd34d7bbee4d48397ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 02:07:17 +0000 Subject: [PATCH 22/22] Bump org.apache.maven:maven-core from 3.9.9 to 3.9.10 Bumps org.apache.maven:maven-core from 3.9.9 to 3.9.10. --- updated-dependencies: - dependency-name: org.apache.maven:maven-core dependency-version: 3.9.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26d9825..d6fad88 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ See the Apache License Version 2.0 for the specific language governing permissio org.apache.maven maven-core - 3.9.9 + 3.9.10 provided