eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

We may wonder how widely recognized IDEs like IntelliJ IDEA and Eclipse implement debugging features. These tools rely heavily on the Java Platform Debugger Architecture (JPDA).

In this introductory article, we’ll discuss the Java Debug Interface API (JDI) available under JPDA.

At the same time, we’ll write a custom debugger program step-by-step, familiarizing ourselves with handy JDI interfaces.

2. Introduction to JPDA

Java Platform Debugger Architecture (JPDA) is a set of well-designed interfaces and protocols used to debug Java.

It provides three specially designed interfaces, to implement custom debuggers for a development environment in desktop systems.

To begin, the Java Virtual Machine Tool Interface (JVMTI) helps us interact and control the execution of applications running in the JVM.

Then, there’s the Java Debug Wire Protocol (JDWP) which defines the protocol used between the application under test (debuggee) and the debugger.

At last, the Java Debug Interface (JDI) is used to implement the debugger application.

3. What Is JDI?

Java Debug Interface API is a set of interfaces provided by Java, to implement the frontend of the debugger. JDI is the highest-layer of the JPDA.

A debugger built with JDI can debug applications running in any JVM which supports JPDA. At the same time, we can hook it into any layer of debugging.

It provides the ability to access the VM and its state along with access to variables of the debuggee. At the same time, it allows to set the breakpoints, stepping, watchpoints and handle threads.

4. Setup

We’ll require two separate programs – a debuggee and a debugger – to understand JDI’s implementations.

First, we’ll write a sample program as the debuggee.

Let’s create a JDIExampleDebuggee class with a few String variables and println statements:

public class JDIExampleDebuggee {
    public static void main(String[] args) {
        String jpda = "Java Platform Debugger Architecture";
        System.out.println("Hi Everyone, Welcome to " + jpda); // add a break point here

        String jdi = "Java Debug Interface"; // add a break point here and also stepping in here
        String text = "Today, we'll dive into " + jdi;
        System.out.println(text);
    }
}

Then, we’ll write a debugger program.

Let’s create a JDIExampleDebugger class with properties to hold the debugging program (debugClass) and line numbers for breakpoints (breakPointLines):

public class JDIExampleDebugger {
    private Class debugClass; 
    private int[] breakPointLines;

    // getters and setters
}

4.1. LaunchingConnector

At first, a debugger requires a connector to establish a connection with the target Virtual Machine (VM).

Then, we’ll need to set the debuggee as the connector’s main argument. At last, the connector should launch the VM for debugging.

To do so, JDI provides a Bootstrap class which gives an instance of the LaunchingConnector. The LaunchingConnector provides a map of the default arguments, in which we can set the main argument.

Therefore, let’s add the connectAndLaunchVM method to the JDIDebuggerExample class:

public VirtualMachine connectAndLaunchVM() throws Exception {
 
    LaunchingConnector launchingConnector = Bootstrap.virtualMachineManager()
      .defaultConnector();
    Map<String, Connector.Argument> arguments = launchingConnector.defaultArguments();
    arguments.get("main").setValue(debugClass.getName());
    return launchingConnector.launch(arguments);
}

Now, we’ll add the main method to the JDIDebuggerExample class to debug the JDIExampleDebuggee:

public static void main(String[] args) throws Exception {
 
    JDIExampleDebugger debuggerInstance = new JDIExampleDebugger();
    debuggerInstance.setDebugClass(JDIExampleDebuggee.class);
    int[] breakPoints = {6, 9};
    debuggerInstance.setBreakPointLines(breakPoints);
    VirtualMachine vm = null;
    try {
        vm = debuggerInstance.connectAndLaunchVM();
        vm.resume();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

Let’s compile both of our classes, JDIExampleDebuggee (debuggee) and JDIExampleDebugger (debugger):

javac -g -cp "/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar" 
com/baeldung/jdi/*.java

Let’s discuss the javac command used here, in detail.

The -g option generates all the debugging information without which, we may see AbsentInformationException.

And -cp will add the tools.jar in the classpath to compile the classes.

All JDI libraries are available under tools.jar of the JDK. Therefore, make sure to add the tools.jar in the classpath at both compilation and execution.

That’s it, now we are ready to execute our custom debugger JDIExampleDebugger:

java -cp "/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar:." 
JDIExampleDebugger

Note the “:.” with tools.jar. This will append tools.jar to the classpath for current run time (use “;.” on windows).

4.2. Bootstrap and ClassPrepareRequest

Executing the debugger program here will give no results since we haven’t prepared the class for debugging and set the breakpoints.

The VirtualMachine class has the eventRequestManager method to create various requests like ClassPrepareRequest, BreakpointRequest, and StepEventRequest.

So, let’s add the enableClassPrepareRequest method to the JDIExampleDebugger class.

This will filter the JDIExampleDebuggee class and enables the ClassPrepareRequest:

public void enableClassPrepareRequest(VirtualMachine vm) {
    ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest();
    classPrepareRequest.addClassFilter(debugClass.getName());
    classPrepareRequest.enable();
}

4.3. ClassPrepareEvent and BreakpointRequest

Once, ClassPrepareRequest for the JDIExampleDebuggee class is enabled, the event queue of the VM will start having instances of the ClassPrepareEvent.

Using ClassPrepareEvent, we can get the location to set a breakpoint and creates a BreakPointRequest.

To do so, let’s add the setBreakPoints method to the JDIExampleDebugger class:

public void setBreakPoints(VirtualMachine vm, ClassPrepareEvent event) throws AbsentInformationException {
    ClassType classType = (ClassType) event.referenceType();
    for(int lineNumber: breakPointLines) {
        Location location = classType.locationsOfLine(lineNumber).get(0);
        BreakpointRequest bpReq = vm.eventRequestManager().createBreakpointRequest(location);
        bpReq.enable();
    }
}

4.4. BreakPointEvent and StackFrame

So far, we’ve prepared the class for debugging and set the breakpoints. Now, we need to catch the BreakPointEvent and display the variables.

JDI provides the StackFrame class, to get the list of all the visible variables of the debuggee.

Therefore, let’s add the displayVariables method to the JDIExampleDebugger class:

public void displayVariables(LocatableEvent event) throws IncompatibleThreadStateException, 
AbsentInformationException {
    StackFrame stackFrame = event.thread().frame(0);
    if(stackFrame.location().toString().contains(debugClass.getName())) {
        Map<LocalVariable, Value> visibleVariables = stackFrame
          .getValues(stackFrame.visibleVariables());
        System.out.println("Variables at " + stackFrame.location().toString() +  " > ");
        for (Map.Entry<LocalVariable, Value> entry : visibleVariables.entrySet()) {
            System.out.println(entry.getKey().name() + " = " + entry.getValue());
        }
    }
}

5. Debug Target

At this step, all we need is to update the main method of the JDIExampleDebugger to start debugging.

Hence, we’ll use the already discussed methods like enableClassPrepareRequest, setBreakPoints, and displayVariables:

try {
    vm = debuggerInstance.connectAndLaunchVM();
    debuggerInstance.enableClassPrepareRequest(vm);
    EventSet eventSet = null;
    while ((eventSet = vm.eventQueue().remove()) != null) {
        for (Event event : eventSet) {
            if (event instanceof ClassPrepareEvent) {
                debuggerInstance.setBreakPoints(vm, (ClassPrepareEvent)event);
            }
            if (event instanceof BreakpointEvent) {
                debuggerInstance.displayVariables((BreakpointEvent) event);
            }
            vm.resume();
        }
    }
} catch (VMDisconnectedException e) {
    System.out.println("Virtual Machine is disconnected.");
} catch (Exception e) {
    e.printStackTrace();
}

Now firstly, let’s compile the JDIDebuggerExample class again with the already discussed javac command.

And last, we’ll execute the debugger program along with all the changes to see the output:

Variables at com.baeldung.jdi.JDIExampleDebuggee:6 > 
args = instance of java.lang.String[0] (id=93)
Variables at com.baeldung.jdi.JDIExampleDebuggee:9 > 
jpda = "Java Platform Debugger Architecture"
args = instance of java.lang.String[0] (id=93)
Virtual Machine is disconnected.

Hurray! We’ve successfully debugged the JDIExampleDebuggee class. At the same time, we’ve displayed the values of the variables at the breakpoint locations (line number 6 and 9).

Therefore, our custom debugger is ready.

5.1. StepRequest

Debugging also requires stepping through the code and checking the state of the variables at subsequent steps. Therefore, we’ll create a step request at the breakpoint.

While creating the instance of the StepRequest, we must provide the size and depth of the step. We’ll define STEP_LINE and STEP_OVER respectively.

Let’s write a method to enable the step request.

For simplicity, we’ll start stepping at the last breakpoint (line number 9):

public void enableStepRequest(VirtualMachine vm, BreakpointEvent event) {
    // enable step request for last break point
    if (event.location().toString().
        contains(debugClass.getName() + ":" + breakPointLines[breakPointLines.length-1])) {
        StepRequest stepRequest = vm.eventRequestManager()
            .createStepRequest(event.thread(), StepRequest.STEP_LINE, StepRequest.STEP_OVER);
        stepRequest.enable();    
    }
}

Now, we can update the main method of the JDIExampleDebugger, to enable the step request when it is a BreakPointEvent:

if (event instanceof BreakpointEvent) {
    debuggerInstance.enableStepRequest(vm, (BreakpointEvent)event);
}

5.2. StepEvent

Similar to the BreakPointEvent, we can also display the variables at the StepEvent.

Let’s update the main method accordingly:

if (event instanceof StepEvent) {
    debuggerInstance.displayVariables((StepEvent) event);
}

At last, we’ll execute the debugger to see the state of the variables while stepping through the code:

Variables at com.baeldung.jdi.JDIExampleDebuggee:6 > 
args = instance of java.lang.String[0] (id=93)
Variables at com.baeldung.jdi.JDIExampleDebuggee:9 > 
args = instance of java.lang.String[0] (id=93)
jpda = "Java Platform Debugger Architecture"
Variables at com.baeldung.jdi.JDIExampleDebuggee:10 > 
args = instance of java.lang.String[0] (id=93)
jpda = "Java Platform Debugger Architecture"
jdi = "Java Debug Interface"
Variables at com.baeldung.jdi.JDIExampleDebuggee:11 > 
args = instance of java.lang.String[0] (id=93)
jpda = "Java Platform Debugger Architecture"
jdi = "Java Debug Interface"
text = "Today, we'll dive into Java Debug Interface"
Variables at com.baeldung.jdi.JDIExampleDebuggee:12 > 
args = instance of java.lang.String[0] (id=93)
jpda = "Java Platform Debugger Architecture"
jdi = "Java Debug Interface"
text = "Today, we'll dive into Java Debug Interface"
Virtual Machine is disconnected.

If we compare the output, we’ll realize that debugger stepped in from line number 9 and displays the variables at all subsequent steps.

6. Read Execution Output

We might notice that println statements of the JDIExampleDebuggee class haven’t been part of the debugger output.

As per the JDI documentation, if we launch the VM through LaunchingConnector, its output and error streams must be read by the Process object.

Therefore, let’s add it to the finally clause of our main method:

finally {
    InputStreamReader reader = new InputStreamReader(vm.process().getInputStream());
    OutputStreamWriter writer = new OutputStreamWriter(System.out);
    char[] buf = new char[512];
    reader.read(buf);
    writer.write(buf);
    writer.flush();
}

Now, executing the debugger program will also add the println statements from the JDIExampleDebuggee class to the debugging output:

Hi Everyone, Welcome to Java Platform Debugger Architecture
Today, we'll dive into Java Debug Interface

7. Conclusion

In this article, we’ve explored the Java Debug Interface (JDI) API available under the Java Platform Debugger Architecture (JPDA).

Along the way, we’ve built a custom debugger utilizing the handy interfaces provided by JDI. At the same time, we’ve also added stepping capability to the debugger.

As this was just an introduction to JDI, it is recommended to look at the implementations of other interfaces available under JDI API.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)