<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gionata</title>
    <description>The latest articles on DEV Community by Gionata (@gionata_legrottaglie).</description>
    <link>https://dev.to/gionata_legrottaglie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4173624%2F22a616db-802b-4dfa-a12f-8dba45ac010c.png</url>
      <title>DEV Community: Gionata</title>
      <link>https://dev.to/gionata_legrottaglie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gionata_legrottaglie"/>
    <language>en</language>
    <item>
      <title>Debugging Gradle tasks in Eclipse, the way IntelliJ does it</title>
      <dc:creator>Gionata</dc:creator>
      <pubDate>Fri, 09 Oct 2026 14:42:44 +0000</pubDate>
      <link>https://dev.to/gionata_legrottaglie/debugging-gradle-tasks-in-eclipse-the-way-intellij-does-it-1a82</link>
      <guid>https://dev.to/gionata_legrottaglie/debugging-gradle-tasks-in-eclipse-the-way-intellij-does-it-1a82</guid>
      <description>&lt;p&gt;Oct 9, 2026 · &lt;a class="mentioned-user" href="https://dev.to/gionata_legrottaglie"&gt;@gionata_legrottaglie&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right-click a Gradle task in Eclipse, choose Debug, and stop at your breakpoints: no Remote Java Application, no fixed port. Building it taught me two things about Eclipse that are worth knowing even if you never install the plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Buildship, the Gradle integration of Eclipse, runs Gradle tasks but cannot debug them. To stop at a breakpoint in code a task starts (&lt;code&gt;run&lt;/code&gt;, &lt;code&gt;bootRun&lt;/code&gt;, a custom &lt;code&gt;JavaExec&lt;/code&gt;, a test), you go through the same routine every time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the task with &lt;code&gt;--debug-jvm&lt;/code&gt;, so that its JVM waits for a debugger on port 5005.&lt;/li&gt;
&lt;li&gt;Create a &lt;em&gt;Remote Java Application&lt;/em&gt; configuration that attaches to &lt;code&gt;localhost:5005&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Launch the two in the right order, or tie them in a &lt;em&gt;Launch Group&lt;/em&gt; that waits for &lt;code&gt;Listening for transport dt_socket&lt;/code&gt; in the console.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It works, but it is a configuration per project, a fixed port that clashes when two apps run, and two launches to stop. In 2016 the Buildship team answered on the &lt;a href="https://discuss.gradle.org/t/gradle-buildship-plugin-with-eclipse-hard-time-finding-debug-option/15325" rel="noopener noreferrer"&gt;Gradle forum&lt;/a&gt; that debugging builds was "on our roadmap". IntelliJ IDEA has always had it: right-click the task, Debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  One right-click
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/legrottagliegionata/eclipse-gradle-debug" rel="noopener noreferrer"&gt;Gradle Task Debugger for Eclipse&lt;/a&gt; adds &lt;strong&gt;Debug Gradle Tasks&lt;/strong&gt; to the context menu of the Gradle Tasks view. Choose it on &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;bootRun&lt;/code&gt; or &lt;code&gt;test&lt;/code&gt;, and the debugger is attached to every JVM the build starts: breakpoints, stepping and variables as in a Java application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run configurations:&lt;/strong&gt; every &lt;em&gt;Gradle Task&lt;/em&gt; configuration of Buildship can also be launched in debug mode, from the Debug toolbar button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debug view:&lt;/strong&gt; a &lt;em&gt;Gradle build&lt;/em&gt; entry shows the build; Terminate on it stops the build and its JVMs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hot code replace:&lt;/strong&gt; saving a Java file updates the running code, including classes with lambdas (more on that below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No port to configure:&lt;/strong&gt; each session uses a free port, so two apps can be debugged at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Screenshots: &lt;a href="https://github.com/legrottagliegionata/eclipse-gradle-debug/blob/main/docs/screenshot-menu.png" rel="noopener noreferrer"&gt;the menu&lt;/a&gt; and &lt;a href="https://github.com/legrottagliegionata/eclipse-gradle-debug/blob/main/docs/screenshot-debug-session.png" rel="noopener noreferrer"&gt;a debug session of the run task&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The trick is to turn the connection around: Eclipse listens, and the JVMs Gradle starts connect to it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Eclipse starts listening for JVMs on a free port, with the &lt;em&gt;Socket Listen&lt;/em&gt; connector of JDT.&lt;/li&gt;
&lt;li&gt;Buildship runs the build exactly as in run mode, plus an init script, added through Buildship's public &lt;code&gt;invocationcustomizers&lt;/code&gt; extension point.&lt;/li&gt;
&lt;li&gt;The init script sets &lt;code&gt;debugOptions&lt;/code&gt; on every task that forks a JVM: JDWP in client mode (&lt;code&gt;server=n&lt;/code&gt;), suspended until the debugger is there.&lt;/li&gt;
&lt;li&gt;Each JVM connects to Eclipse when it starts; when the build ends, Eclipse stops listening.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The heart of the init script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;gradle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allprojects&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JavaForkOptions&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;configureEach&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debugOptions&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// the JVM connects to Eclipse&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;suspend&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;systemProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gradleTaskDebugger.port'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details matter. &lt;code&gt;set()&lt;/code&gt; wins over the conventions a build plugin may have configured for its own debug options. And the port is read lazily from a system property, so the configuration cache is reused from one debug session to the next, even though the port changes every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: Eclipse stops compiling while a Gradle task runs
&lt;/h2&gt;

&lt;p&gt;The first version debugged fine, but hot code replace did nothing. I saved a change, and there was no error and no &lt;em&gt;out of synch&lt;/em&gt; marker. The class file Eclipse compiles into &lt;code&gt;bin/main&lt;/code&gt; still had the timestamp of before the save.&lt;/p&gt;

&lt;p&gt;The cause is in Buildship: it runs every build inside a workspace operation (&lt;code&gt;IWorkspace.run&lt;/code&gt;), and Eclipse's auto-build waits for that operation to end. A task like &lt;code&gt;bootRun&lt;/code&gt;, which runs a server, ends only when you stop it. Until then Eclipse does not compile what you save, so there is no new class for hot code replace to send. Plain &lt;em&gt;Run&lt;/em&gt; behaves the same; you only notice it when you debug.&lt;/p&gt;

&lt;p&gt;An explicit incremental build is not held back, and that is the way out. I first proved it with a test that saves a file and relies on &lt;em&gt;Build Automatically&lt;/em&gt;: it failed with Buildship's behavior and passed once the plugin built on save.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: javac and ECJ do not agree on lambda names
&lt;/h2&gt;

&lt;p&gt;With Eclipse compiling again, the next save of a one-line change to a &lt;code&gt;println&lt;/code&gt; gave &lt;em&gt;Hot code replace failed - Delete method not implemented&lt;/em&gt;. No method had been deleted.&lt;/p&gt;

&lt;p&gt;The JVM was running the class Gradle compiled, with javac. Hot code replace sends the class Eclipse compiled, with its own compiler, ECJ. The two compile lambdas into synthetic methods with different names. In a REST endpoint with two lambdas in a method &lt;code&gt;findCustomer&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Compiler&lt;/th&gt;
&lt;th&gt;Synthetic methods of the two lambdas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;javac (Gradle): the class in the JVM&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lambda$findCustomer$0&lt;/code&gt;, &lt;code&gt;lambda$findCustomer$1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECJ (Eclipse): the class hot code replace sends&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lambda$1&lt;/code&gt;, &lt;code&gt;lambda$2&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To the JVM, the new class deletes two methods and adds two, and class redefinition allows neither. So any class with a lambda fails this way, whatever you change in it; enum switches and anonymous classes can do the same. In a &lt;em&gt;Java Application&lt;/em&gt; launch you never see it, because the JVM runs ECJ's classes too. You can check any class yourself with &lt;code&gt;javap -p&lt;/code&gt; on &lt;code&gt;build/classes&lt;/code&gt; and on &lt;code&gt;bin/main&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: reload what Gradle compiles
&lt;/h2&gt;

&lt;p&gt;If the JVM runs javac's classes, hot code replace must send javac's classes. So during a debug session, saving a Java file does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The plugin runs the &lt;code&gt;classes&lt;/code&gt; task of that file's project with Gradle, in the background.&lt;/li&gt;
&lt;li&gt;It collects the class files Gradle rewrote under &lt;code&gt;build/classes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It hands them to JDT's own hot code replace, which redefines them in the JVMs, reinstalls the breakpoints and drops the obsolete frames, as it does for the classes Eclipse compiles.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Same compiler on both sides, so lambdas, enum switches and anonymous classes reload like any other change. The cost is one Gradle compilation per save: about 4 seconds in the plugin's integration test, a little more on a large module. The limits of the JVM stay: adding or removing methods or fields still needs a restart, and JDT says so as usual. It is the same trade-off IntelliJ IDEA makes when it delegates the build to Gradle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The plugin is open source (EPL 2.0) and needs Eclipse 2024-06 or later with Buildship 3.1, which the Eclipse IDE packages include.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; search for &lt;em&gt;Gradle Task Debugger&lt;/em&gt; in &lt;em&gt;Help → Eclipse Marketplace…&lt;/em&gt;, or install from the &lt;a href="https://marketplace.eclipse.org/content/gradle-task-debugger-eclipse" rel="noopener noreferrer"&gt;Marketplace page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update site:&lt;/strong&gt; &lt;code&gt;https://legrottagliegionata.github.io/eclipse-gradle-debug/&lt;/code&gt;, signed with PGP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo:&lt;/strong&gt; the repository has a small &lt;a href="https://github.com/legrottagliegionata/eclipse-gradle-debug/blob/main/demo/README.md" rel="noopener noreferrer"&gt;demo project&lt;/a&gt; whose &lt;code&gt;run&lt;/code&gt; task starts an HTTP server: import it, put a breakpoint, right-click &lt;code&gt;run&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback:&lt;/strong&gt; bugs and ideas go to the &lt;a href="https://github.com/legrottagliegionata/eclipse-gradle-debug/issues" rel="noopener noreferrer"&gt;GitHub issues&lt;/a&gt;; a star on the repository helps others find it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One limit to know: only the JVMs the build starts are debugged. Build logic (plugins, &lt;code&gt;buildSrc&lt;/code&gt;) runs in the Gradle daemon; for that, Gradle's own &lt;code&gt;-Dorg.gradle.debug=true&lt;/code&gt; still applies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://discuss.gradle.org/t/gradle-buildship-plugin-with-eclipse-hard-time-finding-debug-option/15325" rel="noopener noreferrer"&gt;Gradle forum: Gradle buildship plugin with eclipse (hard time finding debug option)&lt;/a&gt;, the 2016 roadmap answer&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discuss.gradle.org/t/debugging-tests-with-buildship/14845" rel="noopener noreferrer"&gt;Gradle forum: Debugging tests with Buildship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fabianlee.org/2022/08/29/gradle-interactive-jdwp-debugging-of-bootrun-gradle-task-in-eclipse-ide/" rel="noopener noreferrer"&gt;Fabian Lee: interactive JDWP debugging of bootRun gradle task in Eclipse IDE&lt;/a&gt;, the manual routine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.gradle.org/current/userguide/troubleshooting.html" rel="noopener noreferrer"&gt;Gradle: Troubleshooting builds&lt;/a&gt;, on &lt;code&gt;org.gradle.debug&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gradle.org/current/userguide/configuration_cache.html" rel="noopener noreferrer"&gt;Gradle: Configuration cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/legrottagliegionata/eclipse-gradle-debug" rel="noopener noreferrer"&gt;Gradle Task Debugger for Eclipse on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>debugging</category>
      <category>java</category>
      <category>tools</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
