diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java index cd9abfbcd0..b184284f38 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java @@ -13,6 +13,7 @@ package org.eclipse.m2e.internal.launch; +import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Map; @@ -20,15 +21,15 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchesListener2; +import org.codehaus.plexus.build.connect.TcpBuildConnection; +import org.codehaus.plexus.build.connect.TcpBuildConnection.ServerConnection; + import org.eclipse.m2e.core.embedder.ArtifactKey; -import org.eclipse.m2e.core.internal.launch.MavenEmbeddedRuntime; import org.eclipse.m2e.internal.launch.MavenRuntimeLaunchSupport.VMArguments; -import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge; import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenBuildConnection; import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenProjectBuildData; @@ -70,23 +71,50 @@ public void launchesChanged(ILaunch[] launches) { // ignore static void openListenerConnection(ILaunch launch, VMArguments arguments) { try { - if(MavenLaunchUtils.getMavenRuntime(launch.getLaunchConfiguration()) instanceof MavenEmbeddedRuntime) { - - Map projects = new ConcurrentHashMap<>(); - - MavenBuildConnection connection = M2EMavenBuildDataBridge.prepareConnection( - launch.getLaunchConfiguration().getName(), - d -> projects.put(new ArtifactKey(d.groupId, d.artifactId, d.version, null), d)); - - if(LAUNCH_PROJECT_DATA.putIfAbsent(launch, new MavenBuildConnectionData(projects, connection)) != null) { - connection.close(); - throw new IllegalStateException( - "Maven bridge already created for launch of" + launch.getLaunchConfiguration().getName()); - } - arguments.append(connection.getMavenVMArguments()); +// if(MavenLaunchUtils.getMavenRuntime(launch.getLaunchConfiguration()) instanceof MavenEmbeddedRuntime) { +// +// Map projects = new ConcurrentHashMap<>(); +// +// MavenBuildConnection connection = M2EMavenBuildDataBridge.prepareConnection( +// launch.getLaunchConfiguration().getName(), +// d -> projects.put(new ArtifactKey(d.groupId, d.artifactId, d.version, null), d)); +// +// if(LAUNCH_PROJECT_DATA.putIfAbsent(launch, new MavenBuildConnectionData(projects, connection)) != null) { +// connection.close(); +// throw new IllegalStateException( +// "Maven bridge already created for launch of" + launch.getLaunchConfiguration().getName()); +// } +// +// arguments.append(connection.getMavenVMArguments()); +// } else { + MavenRunState state = new MavenRunState(); + ServerConnection con = TcpBuildConnection.createServer(msg -> { + System.out.println(msg); + return state.handleMessage(msg); + }); + state.getProject(new ArtifactKey("test", "me", "0.0.1-SNAPSHOT", null)).thenAccept(rp -> rp.onStart(() -> { + System.out.println("project is started!!"); + })); + File location = getJarLocation(); + if(location != null) { + //TODO see bug https://issues.apache.org/jira/browse/MNG-8112 + arguments.appendProperty("maven.ext.class.path", location.getAbsolutePath()); } - } catch(CoreException | IOException ex) { // ignore + con.setupProcess(arguments::appendProperty); +// } + } catch(Exception ex) { // ignore + ex.printStackTrace(); + } + } + + private static File getJarLocation() { + try { + return new File(TcpBuildConnection.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + } catch(Exception e) { } + //TODO better way to find it?!? + //Maybe just consume as a (wrapped) bundle as we only need it to start the server not on the maven classpath! + return null; } static MavenProjectBuildData getBuildProject(ILaunch launch, String groupId, String artifactId, String version) { diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenRunState.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenRunState.java new file mode 100644 index 0000000000..869d8cf2f0 --- /dev/null +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenRunState.java @@ -0,0 +1,95 @@ +/******************************************************************************** + * Copyright (c) 2025 Christoph Läubrich and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + ********************************************************************************/ + +package org.eclipse.m2e.internal.launch; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import org.codehaus.plexus.build.connect.Configuration; +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; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * Captures the run state of a maven process + */ +public class MavenRunState { + + private String mavenName; + + private String mavenBuildNumber; + + private String mavenVersion; + + private String workingDirectory; + + private final Map> projects = new ConcurrentHashMap<>(); + + Map handleMessage(Message msg) { + if(msg instanceof InitMessage init) { + this.mavenName = init.getProperty("versionProperties.distributionName"); + this.mavenBuildNumber = init.getProperty("versionProperties.buildNumber"); + this.mavenVersion = init.getProperty("versionProperties.version"); + this.workingDirectory = init.getProperty("workingDirectory"); + Map config = new HashMap(); + config.put(Configuration.CONFIG_SEND_PROJECTS, "true"); + return config; + } + if(msg instanceof ProjectsMessage projects) { + projects.projects().forEach(pi -> { + RemoteMavenProject project = new RemoteMavenProject(pi); + getProject(project.getArtifactKey()).complete(project); + }); + } + if(msg instanceof ProjectMessage project) { + ArtifactKey key = new ArtifactKey(project.getGroupId(), project.getArtifactId(), project.getVersion(), null); + getProject(key).thenAccept(rp -> rp.handleEvent(project)); + System.out.println("--- " + project.getType() + " --- " + key); + System.out.println(); + System.out.println(project.getBaseDir()); + return null; + } + if(msg instanceof MojoMessage mojo) { + System.out.println("--- " + mojo.getType() + " ---"); + System.out.println(mojo.getGroupId() + ":" + mojo.getArtifactId() + ":" + mojo.getVersion() + " - " + + mojo.getLifecyclePhase() + " [" + mojo.getExecutionId() + "] " + mojo.getGoal()); + return null; + } + if(msg instanceof SessionMessage session) { + if(!session.isSessionStart()) { + projects.values().forEach(cf -> { + cf.thenAccept(rp -> rp.cancel()); + cf.cancel(true); + }); + projects.clear(); + } + } + return null; + } + + public CompletableFuture getProject(ArtifactKey key) { + CompletableFuture computeIfAbsent = projects.computeIfAbsent(key, + nil -> new CompletableFuture<>()); + return computeIfAbsent; + } + +} diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/RemoteMavenProject.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/RemoteMavenProject.java new file mode 100644 index 0000000000..9fd0b1d5fa --- /dev/null +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/RemoteMavenProject.java @@ -0,0 +1,117 @@ +/******************************************************************************** + * Copyright (c) 2025 Christoph Läubrich and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + ********************************************************************************/ + +package org.eclipse.m2e.internal.launch; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import org.codehaus.plexus.build.connect.messages.ProjectMessage; +import org.codehaus.plexus.build.connect.messages.ProjectMessage.EventType; +import org.codehaus.plexus.build.connect.messages.ProjectsMessage.ProjectInfo; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.DefaultModelReader; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * Represents a maven project from a remote maven launch + */ +public class RemoteMavenProject { + + private ArtifactKey artifactKey; + + private Path baseDir; + + private String modelString; + + private Model model; + + private CompletableFuture starting = new CompletableFuture<>(); + + private CompletableFuture finished = new CompletableFuture<>(); + + RemoteMavenProject(ProjectInfo projectInfo) { + artifactKey = new ArtifactKey(projectInfo.getGroupId(), projectInfo.getArtifactId(), projectInfo.getVersion(), + null); + this.baseDir = projectInfo.getBaseDir(); + this.modelString = projectInfo.getModel(); + } + + /** + * @return the artifactKey + */ + public ArtifactKey getArtifactKey() { + return this.artifactKey; + } + + /** + * @return the baseDir + */ + public Path getBaseDir() { + return this.baseDir; + } + + public synchronized Model getModel() { + if(model != null) { + return model; + } + try { + if(modelString == null) { + return model = new Model(); + } + try { + return model = new DefaultModelReader().read(new StringReader(modelString), null); + } catch(IOException ex) { + return model = new Model(); + } + } finally { + model = null; + } + } + + public CompletableFuture onStart(Runnable runnable) { + return starting.thenAcceptAsync(nil -> runnable.run()); + } + + public CompletableFuture onFinish(Consumer consumer) { + return finished.thenAcceptAsync(result -> consumer.accept(result)); + } + + void handleEvent(ProjectMessage project) { + if(project.getType() == EventType.ProjectStarted) { + starting.complete(null); + } else if(project.getType() == EventType.ProjectSucceeded) { + finished.complete(Result.SUCCESS); + } else if(project.getType() == EventType.ProjectSkipped) { + finished.complete(Result.SKIPPED); + } else if(project.getType() == EventType.ProjectFailed) { + finished.complete(Result.FAILED); + } + } + + void cancel() { + starting.cancel(true); + finished.cancel(true); + } + + public static enum Result { + SUCCESS, FAILED, SKIPPED; + } + +} diff --git a/org.eclipse.m2e.maven.runtime/pom.xml b/org.eclipse.m2e.maven.runtime/pom.xml index 8c9c800819..8cb8262963 100644 --- a/org.eclipse.m2e.maven.runtime/pom.xml +++ b/org.eclipse.m2e.maven.runtime/pom.xml @@ -21,7 +21,7 @@ org.eclipse.m2e.maven.runtime - 3.9.900-SNAPSHOT + 3.9.901-SNAPSHOT jar M2E Embedded Maven Runtime (includes Incubating components) @@ -30,7 +30,7 @@ 3.9.9 - 1.2.0 + 1.2.1-SNAPSHOT target/jars ${project.build.directory}/classes-source false diff --git a/pom.xml b/pom.xml index 3ffe83e7c3..e44d1cb5e5 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ org.eclipse.m2e org.eclipse.m2e.maven.runtime - 3.9.900-SNAPSHOT + 3.9.901-SNAPSHOT