IntelliJ IDEA 2020.2 Help

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 inside a Docker container with OpenJDK 8 and then debug it by setting breakpoints in the source code and using a remote debug configuration.

For this tutorial, you will need a running Docker daemon and connect IntelliJ IDEA to it as described in Enable Docker support.

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 the 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. The methods for verifying and changing the password are not implemented for the purposes of this tutorial.

  1. Create a new empty Java project that uses JDK 8.

  2. Add the JavaPassFromConsole class in the src directory with the following code:

    import java.io.Console; import java.util.Arrays; public class JavaPassFromConsole { public static void main (String[] args) { Console c = System.console(); String login = c.readLine("Enter your login: "); char[] oldPassword = c.readPassword("Enter your old password: "); if (verify(login, oldPassword)) { boolean match =false; while(!match) { char[] newPassword1 = c.readPassword("Enter your new password: "); char[] newPassword2 = c.readPassword("Enter new password again: "); match = Arrays.equals(newPassword1, newPassword2); if (match) { change(login, newPassword1); c.format("Password for %s changed.%n", login); } else { c.format("Passwords don't match. Try again.%n"); } Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); } } 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) { } }

    If you try to run this application in IntelliJ IDEA, it will throw a NullPointerException. This happens because the IDE runs Java applications with the javaw command: without an associated console. As a result, System.console() returns null. You can try to compile the JavaPassFromConsole.java file and run it using the java command from the command line to see that the code is actually correct.

  3. From the main menu, select File | Project Structure Ctrl+Alt+Shift+S.

  4. In the Project Structure dialog, select Artifacts, click the Add button and select JAR | From modules with dependencies.

  5. In the Create JAR from Modules dialog, specify JavaPassFromConsole as the Main Class and click OK.

    Add a JAR artifact
  6. From the main menu, select Build | Build Artifacts. Then select Build for the JavaPassFromConsole:jar artifact. IntelliJ IDEA builds the artifact to out/artifacts/JavaPassFromConsole_jar/JavaPassFromConsole.jar.

Create a Dockerfile and a Docker run configuration

  1. In the Project tool window, right-click the src directory and select New | File.

  2. Type Dockerfile as the name of the file and add the following to it:

    FROM openjdk:8 COPY . /tmp WORKDIR /tmp CMD ["java", "-jar", "JavaPassFromConsole.jar"]

    These instructions tell Docker to run a container from the openjdk:8 image, which contains the Java 8 JDK. Then it copies the current directory (we will set it to be the artifact output directory) to /tmp inside the container. Then it changes the working directory to /tmp and runs the following command:

    java -jar JavaPassFromConsole.jar
  3. Click the Run button in the gutter and select New Run Configuration.

  4. In the Edit Run Configuration dialog, change the context folder to out/artifacts/JavaPassFromConsole_jar (where the JAR artifact is built to), give a name for the container (for example, MyPassAppContainer), and specify the command to run your application: java -jar JavaPassFromConsole.jar.

    Docker run configuration from Dockerfile
  5. Click Run to check that the application works correctly inside a Docker container. When the container starts, select it in the Services tool window and open the Attached Console tab. You should see the prompt with the words Enter your login:.

    The Services tool window with a running container

Create a remote debug configuration

To attach the debugger to the running application, you need a remote debug configuration. Before starting, it should first launch the Docker run configuration and start the application in debug mode.

  1. From the main menu, select Run | Edit Configurations.

  2. Click the Add New Configuration button and select Remote.

  3. In the Before launch section, click the Add button and select Launch Docker before debug.

  4. Select the Docker configuration that runs your app and specify the command to use when running your app in the Custom Command field. The remote debug configuration will use this custom command instead of the one defined in the Dockerfile. This command should contain the -agentlib option to let the debugger attach to the process:

    java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -jar JavaPassFromConsole.jar
    Remote debug configuration with Docker configuration

    Make sure that the ports in the Configure Docker dialog and in the remote debug configuration match. Click OK.

  5. 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 JavaPassFromConsole class)

  • The built JAR file in out/artifacts/JavaPassFromConsole_jar/JavaPassFromConsole.jar

  • The Docker run configuration that runs the application in a container based on the Dockerfile

  • 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.

  1. Open the JavaPassFromConsole.java file and set breakpoints at key points in the code:

    Set breakpoints
  2. Select the remote debug configuration in the main toolbar and click the Debug button.

    Remote debug configuration selected

    Alternatively, you can press Alt+Shift+F9 and select the remote debug configuration.

It should now rebuild the image from the Dockerfile and start the container with the application. The debugger should attach to the running application and show you the Debug tool window. When the application hits the first breakpoint (at the Enter your login: prompt), the debugger will suspend the application and you can analyze the current state. To step over to the next statement in you code, click ${iconAlt} or resume the execution of the application until it hits the next breakpoint with the Resume button. You can switch to the Services tool window to interact with the application in the container's attached console.

Last modified: 19 August 2020