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

[Launching] automatically debug forked JVMs in Debug Maven build (#124) #471

Draft
wants to merge 5 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
2 changes: 1 addition & 1 deletion m2e-maven-runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
<_nodefaultversion>true</_nodefaultversion>
<_snapshot>qualifier</_snapshot>

<Bundle-SymbolicName>${project.artifactId};singleton:=false</Bundle-SymbolicName>
<Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
<Bundle-RequiredExecutionEnvironment>JavaSE-11</Bundle-RequiredExecutionEnvironment>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-Vendor>Eclipse.org - m2e</Bundle-Vendor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private static void assertLinkActivationOpensPartWithTitle(ConsoleHyperlinkPosit
}

@Test
public void testConsole_automaticDebuggerAttachment() throws Exception {
public void testConsole_automaticDebuggerAttachment_explicit() throws Exception {

importMavenProjectIntoWorkspace(SIMPLE_PROJECT);

Expand All @@ -187,6 +187,16 @@ public void testConsole_automaticDebuggerAttachment() throws Exception {
assertDebugeePrintOutAndDebuggerLaunch(document, SIMPLE_PROJECT);
}

@Test
public void testConsole_automaticDebuggerAttachment_fromDebugLaunch() throws Exception {

importMavenProjectIntoWorkspace(SIMPLE_PROJECT);

IDocument document = runMavenBuild(projectLocVariable(SIMPLE_PROJECT), ILaunchManager.DEBUG_MODE);

assertDebugeePrintOutAndDebuggerLaunch(document, SIMPLE_PROJECT);
}

private static void assertDebugeePrintOutAndDebuggerLaunch(IDocument document, String debugLaunchName)
throws CoreException {
// Check for Listening debugee print-out
Expand Down
7 changes: 7 additions & 0 deletions org.eclipse.m2e.debug/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
34 changes: 34 additions & 0 deletions org.eclipse.m2e.debug/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.m2e.debug</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
10 changes: 10 additions & 0 deletions org.eclipse.m2e.debug/.settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11
4 changes: 4 additions & 0 deletions org.eclipse.m2e.debug/.settings/org.eclipse.m2e.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
14 changes: 14 additions & 0 deletions org.eclipse.m2e.debug/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: M2E Debug Support
Bundle-SymbolicName: org.eclipse.m2e.debug;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Eclipse.org - m2e
Fragment-Host: org.eclipse.m2e.maven.runtime
Automatic-Module-Name: org.eclipse.m2e.debug
Bundle-RequiredExecutionEnvironment: JavaSE-11
Import-Package: javax.inject;version="1.0.0"
Require-Bundle: org.eclipse.m2e.launching,
org.eclipse.debug.core,
org.eclipse.equinox.common,
org.eclipse.m2e.core
2 changes: 2 additions & 0 deletions org.eclipse.m2e.debug/META-INF/sisu/javax.inject.Named
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.eclipse.m2e.debug.runtime.DebugEventSpy
org.eclipse.m2e.debug.runtime.DebugExecutionListener
5 changes: 5 additions & 0 deletions org.eclipse.m2e.debug/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
fragment.xml
14 changes: 14 additions & 0 deletions org.eclipse.m2e.debug/fragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<fragment>
<extension
point="org.eclipse.m2e.launching.mavenLaunchParticipants">
<mavenLaunchParticipant
class="org.eclipse.m2e.debug.setup.DebugLaunchParticipant"
id="org.eclipse.m2e.debug.setup.DebugLaunchParticipant"
modes="debug"
name="M2E Debug Support Launch Participant">
</mavenLaunchParticipant>
</extension>

</fragment>
48 changes: 48 additions & 0 deletions org.eclipse.m2e.debug/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2021, 2021 Hannes Wellmann and others
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/

SPDX-License-Identifier: EPL-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.m2e</groupId>
<artifactId>m2e-core</artifactId>
<version>1.16.0-SNAPSHOT</version>
</parent>

<artifactId>org.eclipse.m2e.debug</artifactId>
<version>1.18.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>

<name>M2E Debug Support for Maven Build Processes</name>

<build>
<plugins>
<plugin>
<groupId>org.eclipse.sisu</groupId>
<artifactId>sisu-maven-plugin</artifactId>
<version>0.3.4</version>
<executions>
<execution>
<?m2e execute onIncremental?>
<id>index-project</id>
<phase>prepare-package</phase>
<goals>
<goal>index</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package org.eclipse.m2e.debug.runtime;

import static java.util.Map.entry;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.function.UnaryOperator;

import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.ExecutionEvent.Type;
import org.apache.maven.plugin.MojoExecution;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named
@Singleton
public class DebugEventSpy extends AbstractEventSpy {
private static final Logger LOGGER = LoggerFactory.getLogger(DebugEventSpy.class);

private static final String DEBUG_PORT_VARIABLE = "${debugPort}";
public static final String DEBUG_DATA_PROPERTY = "m2e.debug.support"; //$NON-NLS-1$

public static String getEnableDebugProperty() {
return "-D" + DebugEventSpy.DEBUG_DATA_PROPERTY + "=" + "true";
}

// TODO: name this entire fragment m2e.debug.extension to reflect that it is an
// extension to the maven-runtime?

// TODO: specify the debug setup of the supported plug-ins at a more suitable
// location. Ideally within the Maven-plugins directly (like for lifecycle
// mappings) LifecycleMappingFactory.readMavenPluginEmbeddedMetadata(), or with
// life-cylce mappins/connectors
// TODO: specify properties per Goal!
private static final Map<String, Entry<String, UnaryOperator<String>>> ID_2_CONFIGURATION_INJECTOR = Map.of(
"org.apache.maven.plugins:maven-surefire-plugin", //
entry("debugForkedProcess",
v -> "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=" + DEBUG_PORT_VARIABLE
+ " -Xnoagent -Djava.compiler=NONE"), // TODO: use Java-9 approach: agentlib:jdwp ?
"org.eclipse.tycho:tycho-surefire-plugin", entry("debugPort", v -> DEBUG_PORT_VARIABLE),
"org.eclipse.tycho.extras:tycho-eclipserun-plugin", //
entry("argLine", // TODO: use not deprecated approach with nested elements in jvmArgs
v -> "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=" + DEBUG_PORT_VARIABLE
+ " -Xnoagent")); // TODO: use Java-9 approach: agentlib:jdwp ?

private boolean isDebugLaunch = false;

@Override
public void init(Context context) throws Exception {
Map<String, Object> data = context.getData();
Object userProperties = data.get("userProperties");
if (userProperties instanceof Properties) {
Properties properties = (Properties) userProperties;
isDebugLaunch = Boolean.parseBoolean(properties.getProperty(DEBUG_DATA_PROPERTY, "false"));
}
if (isDebugLaunch) {
LOGGER.debug("M2E - Automatic debugging of forked VMs enabled");
}
}

@Override
public void close() throws Exception {
isDebugLaunch = false;
}

@Override
public void onEvent(Object event) throws Exception {
if (isDebugLaunch && event instanceof ExecutionEvent) {
ExecutionEvent executionEvent = (ExecutionEvent) event;
if (executionEvent.getType() == Type.MojoStarted) {
MojoExecution mojoExecution = executionEvent.getMojoExecution();
String id = mojoExecution.getGroupId() + ":" + mojoExecution.getArtifactId();

Entry<String, UnaryOperator<String>> injector = ID_2_CONFIGURATION_INJECTOR.get(id);
if (injector != null) {
Xpp3Dom configuration = mojoExecution.getConfiguration();
int debugPort = getFreeDebugPort();
String debugElementName = injector.getKey();
Xpp3Dom debugElement = getOrCreateChild(configuration, debugElementName);
injectDebugSetupIntoConfiguration(debugElement, injector.getValue(), debugPort);

LOGGER.debug("M2E - Injected debug-port {} into configuration element <{}>", debugPort,
debugElementName);
}
}
}
}

private void injectDebugSetupIntoConfiguration(Xpp3Dom debugElement, UnaryOperator<String> valueComputer,
int debugPort) {
String enhancedValue = valueComputer.apply(debugElement.getValue());
enhancedValue = enhancedValue.replace(DEBUG_PORT_VARIABLE, Integer.toString(debugPort));
debugElement.setValue(enhancedValue);
// TODO: handle other cases
// - nested elements
// - set(but do not overwrite),
// - append (check if already contained?)
}

private static Xpp3Dom getOrCreateChild(Xpp3Dom configuration, String name) {
Xpp3Dom child = configuration.getChild(name);
if (child != null) {
child.setInputLocation(null); // disconnect from actual source
} else {
child = new Xpp3Dom(name);
configuration.addChild(child);
}
return child;
}

private static int getFreeDebugPort() {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
} catch (IOException e) {
return 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.eclipse.m2e.debug.runtime;

import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.maven.execution.MojoExecutionEvent;
import org.apache.maven.execution.MojoExecutionListener;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecutionException;

@Named
@Singleton
public class DebugExecutionListener implements MojoExecutionListener {
// TODO: this class is likely not necessary anymore

@Override
public void beforeMojoExecution(MojoExecutionEvent event) throws MojoExecutionException {
Mojo mojo = event.getMojo();
}

@Override
public void afterMojoExecutionSuccess(MojoExecutionEvent event) throws MojoExecutionException {
// TODO Auto-generated method stub
}

@Override
public void afterExecutionFailure(MojoExecutionEvent event) {
// TODO Auto-generated method stub
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.eclipse.m2e.debug.setup;

import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
import org.eclipse.m2e.core.internal.launch.MavenEmbeddedRuntime;
import org.eclipse.m2e.debug.runtime.DebugEventSpy;
import org.eclipse.m2e.internal.launch.IMavenLaunchParticipant;
import org.eclipse.m2e.internal.launch.MavenLaunchUtils;

@SuppressWarnings("restriction")
public class DebugLaunchParticipant implements IMavenLaunchParticipant {

@Override
public String getProgramArguments(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) {
try {
if (MavenLaunchUtils.getMavenRuntime(configuration) instanceof MavenEmbeddedRuntime) {
return DebugEventSpy.getEnableDebugProperty();
}
} catch (CoreException e) { // assume false
}
return null;
}

@Override
public String getVMArguments(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) {
// TODO Auto-generated method stub
return null;
}

@Override
public List<ISourceLookupParticipant> getSourceLookupParticipants(ILaunchConfiguration configuration,
ILaunch launch, IProgressMonitor monitor) {
// TODO Auto-generated method stub
return null;
}

}
Loading