I am running the equivalent of
docker run --name james_run -p "25:25" -p "465:465" -p "587:587" -p "143:143" -p "993:993" --volume "$PWD/conf:/root/conf/" --volume "$PWD/var:/root/var/" apache/james:latest
in TestContainers
james = new GenericContainer(DockerImageName.parse("apache/james")).withExposedPorts(25, 465, 587, 143, 993, 8000).withFileSystemBind(EmailServerIT.class.getClassLoader().getResource("conf").getPath(),"/root/conf", BindMode.READ_WRITE).withFileSystemBind(EmailServerIT.class.getClassLoader().getResource("var").getPath(),"/root/var", BindMode.READ_WRITE);
But am getting a connection refused error whenever I try to send to the SMTP port:
Caused by: javax.mail.MessagingException: Connection error (java.net.ConnectException: Connection refused (Connection refused))
I have set to try and avoid needing a keystore but am having no luck.
Please advise
DemoTestContainerLab
├── pom.xml
└── src
└── test
├── java
│ └── example
│ └── SimpleJamesTest.java
└── resources
├── logback-test.xml
└── testcontainers.properties
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>DemoTestContainerLab</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>1.7.25</slf4j.version>
<logback.version>1.2.3</logback.version>
<junit-jupiter.version>5.10.0</junit-jupiter.version>
<testcontainers.version>1.19.0</testcontainers.version>
<jakarta.mail.version>2.1.2</jakarta.mail.version>
</properties>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>${jakarta.mail.version}</version>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>${jakarta.mail.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</project>
package example;
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.Properties;
@Testcontainers
public class SimpleJamesTest {
@Container
private static final GenericContainer<?> james = new GenericContainer<>("apache/james:demo-3.8.0")
.withExposedPorts(143,25,4000,465,587,80,8000,993)
.waitingFor(Wait.forLogMessage(".*AddUser command executed sucessfully.*", 3));;
@Test
public void test1() throws Exception {
james.start();
Integer smtpPort = james.getMappedPort(25);
String to = "user01@james.local";
String from = "user01@james.local";
final String username = "user01@james.local";
final String password = "1234";
String host = "localhost";
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
//props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", smtpPort );
props.put("mail.debug", "true");
Authenticator authenticator = new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};
Session session = Session.getInstance(props, authenticator);
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(to));
message.setSubject("This is a Demo Jakarta Mail!");
message.setText("Just test Jakarta Mail use smtp protocol sned mail");
Transport.send(message);
System.out.println("Email Message Sent Successfully");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
docker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
DOCKER_HOST=unix:///var/run/docker.sock
I use docker image is this: apache/james:demo-3.8.0
It will auto create mail domain, mail user.
Mail Domain:
james.local
Mail user:
user01@james.local
user02@james.local
user03@james.local
Run command: docker inspect apache/james:demo-3.8.0
, find exposed ports.
"ExposedPorts": {
"143/tcp": {},
"25/tcp": {},
"4000/tcp": {},
"465/tcp": {},
"587/tcp": {},
"80/tcp": {},
"8000/tcp": {},
"993/tcp": {}
},
then use it in code:
.withExposedPorts(143,25,4000,465,587,80,8000,993)
If we use command run docker
sudo docker run -it apache/james:demo-3.8.0
we will see the last message is :
11:27:29.319 [INFO ] o.a.j.GuiceJamesServer - JAMES server started
wait-for-it.sh: localhost:9999 is available after 8 seconds
AddDomain command executed sucessfully in 271 ms.
AddUser command executed sucessfully in 311 ms.
AddUser command executed sucessfully in 261 ms.
AddUser command executed sucessfully in 219 ms.
We must wait container add user 3 times.
then use it in our code:
.waitingFor(Wait.forLogMessage(".*AddUser command executed sucessfully.*", 3));;
And TestContainers will mapped original smtp port 25 to some other port in host.
We need use code to get it:
Integer smtpPort = james.getMappedPort(25);
Then we can use it in our smtp send mail code.
props.put("mail.smtp.port", smtpPort );
Run Test Command
mvn test
Run Result - SMTP Debug Message
... skip some messages ...
MAIL FROM:<user01@james.local>
250 2.1.0 Sender <user01@james.local> OK
RCPT TO:<user01@james.local>
250 2.1.5 Recipient <user01@james.local> OK
DEBUG SMTP: Verified Addresses
DEBUG SMTP: user01@james.local
DATA
354 Ok Send data ending with <CRLF>.<CRLF>
Date: Wed, 27 Sep 2023 19:49:14 +0800 (CST)
From: user01@james.local
To: user01@james.local
Message-ID: <471004142.0.1695815354250@demo-pc-ae23f827>
Subject: This is a Demo Jakarta Mail!
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Just test Jakarta Mail use smtp protocol sned mail
.
250 2.6.0 Message received
DEBUG SMTP: message successfully delivered to mail server
QUIT
221 2.0.0 473f3c567c98 Service closing transmission channel
Email Message Sent Successfully