Debug a Java application using a Dockerfile
You can use IntelliJ IDEA to debug a Java application running in a Docker container. This tutorial describes how to create a Dockerfile for running a simple Java console application in a container with OpenJDK 8 and then debug it by setting breakpoints in the source code and using a remote debug configuration.
Create a sample Java console application
The sample application for this tutorial emulates a console utility to change the password. It asks to enter the login name and current password. After verifying the credentials, it asks to type the new password twice. If the passwords don't match, it asks for the new password again, until they match, and then changes the password. Actual methods for verifying and changing the password are not implemented for the purposes of this tutorial.
-
Create a Java class with the following code:
import java.io.Console; import java.io.IOException; import java.util.Arrays; public class Java_pass_from_console { public static void main (String args[]) throws IOException { Console c = System.console(); String login = c.readLine("Enter your login: "); char [] oldPassword = c.readPassword("Enter your old password: "); if (verify(login, oldPassword)) { boolean noMatch; do { char [] newPassword1 = c.readPassword("Enter your new password: "); char [] newPassword2 = c.readPassword("Enter new password again: "); noMatch = ! Arrays.equals(newPassword1, newPassword2); if (noMatch) { c.format("Passwords don't match. Try again.%n"); } else { change(login, newPassword1); c.format("Password for %s changed.%n", login); } Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); } while (noMatch); } Arrays.fill(oldPassword, ' '); } // Dummy verify method static boolean verify(String login, char[] password) { return true; } // Dummy change method static void change(String login, char[] password) { } } Build the application into a JAR file inside your project: out/artifacts/pass/pass.jar
Create a Dockerfile and a run configuration to run the application
-
Create a Dockerfile with the following code:
FROM openjdk:8 COPY . /tmp WORKDIR /tmp CMD ["java", "-jar", "pass.jar"] -
Click in the gutter and select New Run Configuration.
In the Edit Run Configuration dialog, change the context folder to out/artifacts/pass/ (where the JAR artifact is built to), add a name for the container (for example, pass), and specify the command to run your application:
java -jar pass.jar
Click Run to check that the application is working correctly. When the container starts, select it in the Docker tool window and open the Attached Console tab. You should see the prompt with the words
Enter your login:
.
Create a remote debug configuration
The debugger attaches to the application using a remote debug configuration.
On the main menu, select
.Click and select Remote.
In the Before launch section, click and select Launch Docker before debug.
Select the Docker configuration that runs your app. Make sure that the ports in the Configure Docker dialog and in the remote debug configuration match. Click OK.
Click OK in the Run/Debug Configurations dialog.
Set breakpoints and debug your application
By this moment you should have the following:
The source code of your application (the Java_pass_from_console class file)
The built JAR file in out/artifacts/pass/pass.jar
The Dockerfile that runs the application using the Docker run configuration
The remote debug configuration that first launches the Docker run configuration and then attaches to the application in the started container
Before starting the debug configuration, you need to set breakpoints in the source code and rebuild the JAR artifact.
-
Open the Java_pass_from_console.java file and set breakpoints at key points in the code:
Rebuild the JAR artifact.
Select the remote debug configuration in the main toolbar and click (or press Shift+Alt+F9 and select the remote debug configuration).
It should now rebuild the image and start the container with the application. When the application hits the first breakpoint (at the Enter your login:
prompt), the debugger will stop it and you can analyze the current state, then step over with until it hits the next breakpoint, and so on.