Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trace test execution directories in the remote maven execution #1700

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion org.eclipse.m2e.launching/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.27.0,4.0.0)",
org.eclipse.m2e.maven.runtime;bundle-version="[3.8.6,4.0.0)",
org.eclipse.m2e.core;bundle-version="[2.0.0,3.0.0)",
org.eclipse.m2e.core.ui;bundle-version="[2.0.0,3.0.0)",
org.eclipse.m2e.workspace.cli;bundle-version="0.1.0"
org.eclipse.m2e.workspace.cli;bundle-version="0.1.0",
org.eclipse.jdt.junit.core,
org.eclipse.unittest.ui;bundle-version="1.1.200"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-Vendor: %Bundle-Vendor
Expand Down
7 changes: 7 additions & 0 deletions org.eclipse.m2e.launching/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -243,5 +243,12 @@
class="org.eclipse.m2e.internal.launch.MavenConsoleLineTracker"
processType="java"/>
</extension>
<extension
point="org.eclipse.unittest.ui.unittestViewSupport">
<viewSupport
class="org.eclipse.m2e.internal.launch.testing.MavenTestViewSupport"
id="org.eclipse.m2e.launching.testViewSupport">
</viewSupport>
</extension>

</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.unittest.ui.ConfigureViewerSupport;

import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.embedder.IMavenExecutableLocation;
Expand All @@ -75,6 +76,10 @@
* @author Eugene Kuleshov
*/
public class ExecutePomAction implements ILaunchShortcut, IExecutableExtension, ILaunchShortcut2 {

public static final ConfigureViewerSupport TEST_RESULT_LISTENER_CONFIGURER = new ConfigureViewerSupport(
"org.eclipse.m2e.launching.testViewSupport");

private static final Logger log = LoggerFactory.getLogger(ExecutePomAction.class);

private boolean showDialog = false;
Expand Down Expand Up @@ -212,7 +217,6 @@ private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, Strin
workingCopy.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_SCOPE, "${project}"); //$NON-NLS-1$
workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_RECURSIVE, true);

setProjectConfiguration(workingCopy, basedir);

// TODO when launching Maven with debugger consider to add the following property
Expand All @@ -237,6 +241,7 @@ private void setProjectConfiguration(ILaunchConfigurationWorkingCopy workingCopy
workingCopy.setAttribute(MavenLaunchConstants.ATTR_PROFILES, selectedProfiles);
}
}
TEST_RESULT_LISTENER_CONFIGURER.apply(workingCopy);
}

private ILaunchConfiguration getLaunchConfiguration(IContainer basedir, String mode) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/********************************************************************************
* Copyright (c) 2024 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.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;

import org.eclipse.m2e.core.embedder.ArtifactKey;
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge;
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenBuildConnection;
import org.eclipse.m2e.internal.maven.listener.MavenBuildListener;
import org.eclipse.m2e.internal.maven.listener.MavenProjectBuildData;
import org.eclipse.m2e.internal.maven.listener.MavenTestEvent;


/**
* A process that represents the remote connection to the maven process
*/
public class MavenBuildConnectionProcess implements IProcess {

private ILaunch launch;

private Map<String, String> attributes = new HashMap<>();

private Map<ArtifactKey, MavenProjectBuildData> projects = new ConcurrentHashMap<>();

private MavenBuildConnection connection;

private List<MavenBuildListener> buildListeners = new CopyOnWriteArrayList<>();

private AtomicBoolean terminated = new AtomicBoolean();

public MavenBuildConnectionProcess(ILaunch launch) {
this.launch = launch;
launch.addProcess(this);
attributes.put(IProcess.ATTR_PROCESS_TYPE, "m2e-build-endpoint");
fireEvent(new DebugEvent(this, DebugEvent.CREATE));
}

public <T> T getAdapter(Class<T> adapter) {
return null;
}

public boolean canTerminate() {
return !isTerminated();
}

public boolean isTerminated() {
return terminated.get() || connection == null;
}

public void terminate() {
if(terminated.compareAndSet(false, true)) {
if(connection != null) {
try {
connection.close();
} catch(IOException ex) {
}
connection = null;
}
for(MavenBuildListener mavenBuildListener : buildListeners) {
mavenBuildListener.close();
}
buildListeners.clear();
fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
}
}

public String getLabel() {
return "M2E Build Listener";
}

public ILaunch getLaunch() {
return launch;
}

public IStreamsProxy getStreamsProxy() {
return null;
}

@Override
public void setAttribute(String key, String value) {
attributes.put(key, value);
fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
}

@Override
public String getAttribute(String key) {
return attributes.get(key);
}

public void addMavenBuildListener(MavenBuildListener listener) {
buildListeners.add(listener);
}

/**
* @param mavenTestRunnerClient
* @return
*/
public void removeMavenBuildListener(MavenBuildListener listener) {
buildListeners.remove(listener);
}

@Override
public int getExitValue() {
return 0;
}

/**
* @return the projects
*/
public Map<ArtifactKey, MavenProjectBuildData> getProjects() {
return this.projects;
}

void connect() throws IOException {
connection = M2EMavenBuildDataBridge.prepareConnection(launch.getLaunchConfiguration().getName(),
new MavenBuildListener() {

@Override
public void projectStarted(MavenProjectBuildData data) {
projects.put(new ArtifactKey(data.groupId, data.artifactId, data.version, null), data);
for(MavenBuildListener mavenBuildListener : buildListeners) {
mavenBuildListener.projectStarted(data);
}
}

@Override
public void onTestEvent(MavenTestEvent mavenTestEvent) {
for(MavenBuildListener mavenBuildListener : buildListeners) {
mavenBuildListener.onTestEvent(mavenTestEvent);
}
}

public void close() {
MavenBuildConnectionProcess.this.terminate();
}
});
}

String getMavenVMArguments() throws IOException {
return connection.getMavenVMArguments();
}

private void fireEvent(DebugEvent event) {
DebugPlugin manager = DebugPlugin.getDefault();
if(manager != null) {
manager.fireDebugEventSet(new DebugEvent[] {event});
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,88 +16,82 @@
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import java.util.Optional;

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.eclipse.debug.core.model.IProcess;

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;
import org.eclipse.m2e.internal.maven.listener.MavenProjectBuildData;


public class MavenBuildProjectDataConnection {

private static record MavenBuildConnectionData(Map<ArtifactKey, MavenProjectBuildData> projects,
MavenBuildConnection connection) {
}

private static final Map<ILaunch, MavenBuildConnectionData> LAUNCH_PROJECT_DATA = new ConcurrentHashMap<>();

static {
DebugPlugin.getDefault().getLaunchManager().addLaunchListener(new ILaunchesListener2() {
public void launchesRemoved(ILaunch[] launches) {
closeServers(Arrays.stream(launches).map(LAUNCH_PROJECT_DATA::remove));
cleanupConnections(launches);
}

public void launchesTerminated(ILaunch[] launches) {
closeServers(Arrays.stream(launches).map(LAUNCH_PROJECT_DATA::get));
}

private static void closeServers(Stream<MavenBuildConnectionData> connectionData) {
connectionData.filter(Objects::nonNull).forEach(c -> {
try {
c.connection().close();
} catch(IOException ex) { // ignore
}
});
cleanupConnections(launches);
}

public void launchesAdded(ILaunch[] launches) { // ignore
}

public void launchesChanged(ILaunch[] launches) { // ignore
}

private void cleanupConnections(ILaunch[] launches) {
Arrays.stream(launches).flatMap(launch -> getConnection(launch).stream()).forEach(con -> {
con.terminate();
});
}
});
}

static void openListenerConnection(ILaunch launch, VMArguments arguments) {
try {
if(MavenLaunchUtils.getMavenRuntime(launch.getLaunchConfiguration()) instanceof MavenEmbeddedRuntime) {

Map<ArtifactKey, MavenProjectBuildData> 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();
getConnection(launch).ifPresent(existing -> {
existing.terminate();
throw new IllegalStateException(
"Maven bridge already created for launch of" + launch.getLaunchConfiguration().getName());
}
arguments.append(connection.getMavenVMArguments());
});
MavenBuildConnectionProcess process = new MavenBuildConnectionProcess(launch);
process.connect();
arguments.append(process.getMavenVMArguments());
}
} catch(CoreException | IOException ex) { // ignore
}
}

public static Optional<MavenBuildConnectionProcess> getConnection(ILaunch launch) {
for(IProcess process : launch.getProcesses()) {
if(process instanceof MavenBuildConnectionProcess connection) {
return Optional.of(connection);
}
}
return Optional.empty();
}

static MavenProjectBuildData getBuildProject(ILaunch launch, String groupId, String artifactId, String version) {
MavenBuildConnectionData build = LAUNCH_PROJECT_DATA.get(launch);
if(build == null) {
Optional<MavenBuildConnectionProcess> connection = getConnection(launch);
if(connection.isEmpty()) {
return null;
}
MavenBuildConnectionProcess process = connection.get();
Map<ArtifactKey, MavenProjectBuildData> projects = process.getProjects();
ArtifactKey key = new ArtifactKey(groupId, artifactId, version, null);
while(true) {
MavenProjectBuildData buildProject = build.projects().get(key);
if(buildProject != null || build.connection().isReadCompleted()) {
MavenProjectBuildData buildProject = projects.get(key);
if(buildProject != null || process.isTerminated()) {
return buildProject;
}
Thread.onSpinWait(); // Await completion of project data read. It has to become available soon, since its GAV was printed on the console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.project.IBuildProjectFileResolver;
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenProjectBuildData;
import org.eclipse.m2e.internal.maven.listener.MavenProjectBuildData;


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.util.SortedMap;
import java.util.TreeMap;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -121,6 +123,15 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
this.programArguments = null;

try {
Bundle bundle = Platform.getBundle("org.eclipse.unittest.ui");
if(bundle != null) {
try {
bundle.start();
} catch(BundleException ex) {
//we tried our best...
}
}
System.out.println(bundle);
this.launchSupport = MavenRuntimeLaunchSupport.create(configuration, monitor);
this.extensionsSupport = MavenLaunchExtensionsSupport.create(configuration, launch);
this.preferencesService = Platform.getPreferencesService();
Expand All @@ -129,7 +140,6 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
log.info(" mvn {}", getProgramArguments(configuration)); //$NON-NLS-1$

extensionsSupport.configureSourceLookup(configuration, launch, monitor);

super.launch(configuration, mode, launch, monitor);
} finally {
this.launch = null;
Expand Down
Loading
Loading