'Java Testcontainers - Cannot connect to exposed port

I implemented a POP3 server and client using javax.mail just to try doing integration testing with Docker. So I created two Docker images based on the openjdk:8-jre image and copied my jars to them and started it. Based on my configuration (see below) it is working. They are talking to each other.

But as want to have multiple integration tests it is going to be tedious to build an image for each one and starting them. Also I don't know how to automate the results. But then I stumbled across TestContainers and it seems that this would be big help when implementing those tests.

So I started to port those tests to TestContainers using my POP3 Server image as a GenericContainer and starting my POP3 Client classes in the JUnit test method. I exposed the port 24999 which my POP3 Server is listening to. But when I try to connect to the server I get the following error:

com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 32782; timeout -1;
  nested exception is:
    java.net.ConnectException: Connection refused
...

There is probably some setting I am missing in TestContainers. Could you please help me.

Here is the code I am using:

public class DockerPop3AutocryptKeyProvidingAndReceivingTest {
    @Test
    public void test() throws InterruptedException {
        GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
                .withExposedPorts(24999);
        
        container.start();
        
        String host = container.getContainerIpAddress();
        String port = container.getFirstMappedPort().toString();

        //The following is simplified, but copied from the working jar used in the Docker Client image/container
        MyPOP3Client client = new MyPOP3Client(host, port);
        client.connect();
        
        container.stop();
    }
}

This is how I create my Docker image:

FROM openjdk:8-jre

ADD build/distributions/MyPOP3Server.tar . #This is where I have packed all the needed files to. It gets unpacked by Docker.
#EXPOSE 24999 #I tried both with and without this expose
WORKDIR /MyPOP3Server/bin
ENTRYPOINT ["sh","MyPOP3Server"] #Executes the shell script which runs java with my jar

This is a simplified version of the code that is running inside the Server Jar:

MyPOP3Server server = new MyPOP3Server();
server.listenToPort(24999);

Please tell me what am I missing. What is wrong here?

Thanks and kind regards.



Solution 1:[1]

Thanks for all of your help. This led me towards the solution. It has been a combination of the missing WaitStrategy and problems with the port mapping.

Here is what I did: 1) In the MyPop3Server.listenToPort(String port) method I added a System.out.println:

public class MyPop3Server {
  public void listenToPort(String port) {
     //simplified: do initialization and listenToPort
     System.out.println("Awaiting Connection...");
  }
}

In my test I added a LogMessageWaitStrategy that listens for "Awaiting Connection"

GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
   .waitingFor(Wait.forLogMessage("Awaiting Connection.*", 1))
   .withExposedPorts(24999);

2) I switched from container.getFirstMappedPort() to

container.getMappedPort(24999);

Here is the whole changed and working test code:

public class DockerPop3AutocryptKeyProvidingAndReceivingTest {
    @Test
    public void test() throws InterruptedException {
        GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
                .waitingFor(Wait.forLogMessage("Awaiting Connection.*", 1))
                .withExposedPorts(24999);

        container.start();

        String host = container.getContainerIpAddress();
        String port = container.getMappedPort(24999).toString();

        //The following is simplified, but copied from the working jar used in the Docker Client image/container
        MyPOP3Client client = new MyPOP3Client(host, port);
        client.connect();

        container.stop();
    }
}

Thank you everyone.

Solution 2:[2]

Try adding a http check.

 new GenericContainer<>("immerfroehlich/emailfilter:latest")
 .withExposedPorts(24999)
 .waitingFor(new HttpWaitStrategy().forPort(24999)
 .withStartupTimeout(Duration.ofMinutes(5)));

There is a possibility that your container starts but you are trying to connect before your server initializes.

Also, register a log appender to see what is going on with the server inside the container.

 .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(
              DockerPop3AutocryptKeyProvidingAndReceivingTest.class)))

Solution 3:[3]

There's some good advice in other answers; I'll augment these with a couple of other tips:

As already suggested:

  • absolutely do add the LogConsumer so that you can see the container's log output - maybe something useful will appear there, now or in the future. It's always good to have.

  • set a breakpoint after the container is started, just before you start your client.

Additionally, I'd hope the following things make the difference. While paused at the breakpoint:

  • run docker ps -a in the terminal
  • firstly, check that your container is running and hasn't exited. If it has exited, have a look at the logs for the container from the terminal.
  • secondly, check the port mapping in the docker ps output. You should see something like 0.0.0.0:32768->24999/tcp (first port number is random, though).
  • evaluate container.getFirstMappedPort() in your IDE and check that the port number you get back is the same as the randomised exposed port. Unless you have a very unusual Docker installation on your local machine, this container should be reachable at localhost: + this port.
  • if you've got this far then it's likely that there's something wrong with either the container or the client code. You could try connecting a different client to the running container - even something like nc could help if you don't have another POP3 client handy.

Another thing to try is to run the container manually, just to reduce the amount of indirection taking place. The Testcontainers code snippet you've given is equivalent to:

docker run -p 24999 immerfroehlich/emailfilter:latest

You might find that this helps you divide up the problem space into smaller pieces.

Solution 4:[4]

Since your mail server and client are running in the container, I think you should connect to port 24999 and not to the mapped port

Solution 5:[5]

Try container.getMappedPort(24999) instead getFirstMappedPort. Probably your docker image exposes a couple of ports.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 NoradX
Solution 2 lecandas
Solution 3 Richard North
Solution 4 Ashis Roy
Solution 5 ygun