Skip to content

Commit afbc9d0

Browse files
committed
#277: Added native support for setting SSL socket factory instance
1 parent d12e38e commit afbc9d0

File tree

7 files changed

+123
-18
lines changed

7 files changed

+123
-18
lines changed

modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerRegularBuilder.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.jetbrains.annotations.NotNull;
88
import org.jetbrains.annotations.Nullable;
99
import javax.mail.Session;
10+
import javax.net.ssl.SSLSocketFactory;
1011

1112
/**
1213
* Default builder for generating Mailer instances. Sets defaults configured for SMTP host, SMTP port, SMTP username, SMTP password and transport
@@ -100,15 +101,35 @@ public interface MailerRegularBuilder<T extends MailerRegularBuilder<?>> extends
100101
/**
101102
* Configures the session with the right property to use your own factory for obtaining SSL connections.
102103
* <p>
103-
* <strong>Note 1:</strong> Sets the property <code>mail.smtp.ssl.socketFactory.class</code> on the Session.
104+
* <strong>Note 1:</strong> Is overridden by custom factory instance if set.
105+
* <p>
106+
* <strong>Note 2:</strong> Sets the property <code>mail.smtp.ssl.socketFactory.class</code> on the Session.
104107
* <br>
105-
* <strong>Note 2:</strong> This breaks your setup if you also use authenticated proxy.
108+
* <strong>Note 3:</strong> This breaks your setup if you also use authenticated proxy.
106109
*
107110
* @param factoryClass The fully qualified name of the factory class. Example: <code>javax.net.ssl.SSLSocketFactory</code>
108111
*
109112
* @see <a href="https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html">Java / Jakarta Mail properties</a>
113+
* @see #withCustomSSLFactoryInstance(SSLSocketFactory)
110114
*/
111115
T withCustomSSLFactory(@Nullable String factoryClass);
116+
117+
/**
118+
* Configures the session with the right property to use your own factory for obtaining SSL connections.
119+
* <p>
120+
* <strong>Note 1:</strong> Overrides custom factory class if set.
121+
* <p>
122+
* <strong>Note 2:</strong> Sets the property <code>mail.smtp.ssl.socketFactory</code> on the Session.
123+
* <br>
124+
* <strong>Note 3:</strong> This breaks your setup if you also use authenticated proxy.
125+
*
126+
* @param sslSocketFactoryInstance An instance of the {@link SSLSocketFactory} class.
127+
*
128+
* @see <a href="https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html">Java / Jakarta Mail properties</a>
129+
* @see #withCustomSSLFactory(String)
130+
*/
131+
@Cli.ExcludeApi(reason = "This API is specifically for Java use")
132+
T withCustomSSLFactoryInstance(@Nullable SSLSocketFactory sslSocketFactoryInstance);
112133

113134
/**
114135
* Builds the actual {@link Mailer} instance with everything configured on this builder instance.

modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/ServerConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import org.jetbrains.annotations.NotNull;
44
import org.jetbrains.annotations.Nullable;
55

6+
import javax.net.ssl.SSLSocketFactory;
7+
68
public interface ServerConfig {
79
@NotNull String getHost();
810
@NotNull Integer getPort();
911
@Nullable String getUsername();
1012
@Nullable String getPassword();
1113
@Nullable String getCustomSSLFactoryClass();
14+
@Nullable SSLSocketFactory getCustomSSLFactoryInstance();
1215
}

modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,10 @@ public static Session createMailSession(@NotNull final ServerConfig serverConfig
152152
if (serverConfig.getUsername() != null) {
153153
props.put(transportStrategy.propertyNameUsername(), serverConfig.getUsername());
154154
}
155-
if (serverConfig.getCustomSSLFactoryClass() != null) {
156-
// https://www.tutorialspoint.com/javamail_api/javamail_api_smtp_servers.htm
155+
// https://www.tutorialspoint.com/javamail_api/javamail_api_smtp_servers.htm
156+
if (serverConfig.getCustomSSLFactoryInstance() != null) {
157+
props.put("mail.smtp.ssl.socketFactory", serverConfig.getCustomSSLFactoryInstance());
158+
} else if (serverConfig.getCustomSSLFactoryClass() != null) {
157159
props.put("mail.smtp.ssl.socketFactory.class", serverConfig.getCustomSSLFactoryClass());
158160
}
159161

modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerRegularBuilderImpl.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import org.simplejavamail.config.ConfigLoader;
1010
import org.simplejavamail.internal.util.SimpleOptional;
1111

12+
import javax.net.ssl.SSLSocketFactory;
13+
1214
import static org.simplejavamail.config.ConfigLoader.Property.CUSTOM_SSLFACTORY_CLASS;
1315
import static org.simplejavamail.config.ConfigLoader.Property.SMTP_HOST;
1416
import static org.simplejavamail.config.ConfigLoader.Property.SMTP_PASSWORD;
@@ -54,7 +56,12 @@ public class MailerRegularBuilderImpl extends MailerGenericBuilderImpl<MailerReg
5456
* @see #withCustomSSLFactory(String)
5557
*/
5658
private String customSSLFactory;
57-
59+
60+
/**
61+
* @see #withCustomSSLFactoryInstance(SSLSocketFactory)
62+
*/
63+
private SSLSocketFactory customSSLFactoryInstance;
64+
5865
/**
5966
* Sets defaults configured for SMTP host, SMTP port, SMTP username, SMTP password and transport strategy.
6067
* <p>
@@ -170,6 +177,15 @@ public MailerRegularBuilderImpl withCustomSSLFactory(@Nullable final String cust
170177
return this;
171178
}
172179

180+
/**
181+
* @see MailerRegularBuilder#withCustomSSLFactoryInstance(SSLSocketFactory)
182+
*/
183+
@Override
184+
public MailerRegularBuilderImpl withCustomSSLFactoryInstance(@Nullable final SSLSocketFactory customSSLFactoryInstance) {
185+
this.customSSLFactoryInstance = customSSLFactoryInstance;
186+
return this;
187+
}
188+
173189
/**
174190
* @see MailerRegularBuilder#buildMailer()
175191
*/
@@ -184,7 +200,7 @@ public Mailer buildMailer() {
184200
ServerConfig buildServerConfig() {
185201
vallidateServerConfig();
186202
final int serverPort = SimpleOptional.ofNullable(port).orElse(transportStrategy.getDefaultServerPort());
187-
return new ServerConfigImpl(assumeNonNull(getHost()), serverPort, getUsername(), getPassword(), customSSLFactory);
203+
return new ServerConfigImpl(assumeNonNull(getHost()), serverPort, getUsername(), getPassword(), customSSLFactory, customSSLFactoryInstance);
188204
}
189205

190206
private void vallidateServerConfig() {

modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/ServerConfigImpl.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import org.jetbrains.annotations.NotNull;
66
import org.jetbrains.annotations.Nullable;
77

8+
import javax.net.ssl.SSLSocketFactory;
9+
810
import static java.lang.String.format;
911
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
1012

@@ -17,13 +19,16 @@ class ServerConfigImpl implements ServerConfig {
1719
@Nullable private final String username;
1820
@Nullable private final String password;
1921
@Nullable private final String customSSLFactoryClass;
22+
@Nullable private final SSLSocketFactory customSSLFactoryInstance;
2023

21-
ServerConfigImpl(@NotNull final String host, @NotNull final Integer port, @Nullable final String username, @Nullable final String password, @Nullable final String customSSLFactoryClass) {
24+
ServerConfigImpl(@NotNull final String host, @NotNull final Integer port, @Nullable final String username, @Nullable final String password, @Nullable final String customSSLFactoryClass,
25+
final @Nullable SSLSocketFactory customSSLFactoryInstance) {
2226
this.host = host;
2327
this.port = port;
2428
this.username = username;
2529
this.password = password;
2630
this.customSSLFactoryClass = customSSLFactoryClass;
31+
this.customSSLFactoryInstance = customSSLFactoryInstance;
2732

2833
if (valueNullOrEmpty(this.username) && !valueNullOrEmpty(this.password)) {
2934
throw new IllegalArgumentException("Password provided but not a username");
@@ -86,4 +91,13 @@ public String getPassword() {
8691
public String getCustomSSLFactoryClass() {
8792
return customSSLFactoryClass;
8893
}
94+
95+
/**
96+
* @see ServerConfig#getCustomSSLFactoryInstance()
97+
*/
98+
@Override
99+
@Nullable
100+
public SSLSocketFactory getCustomSSLFactoryInstance() {
101+
return customSSLFactoryInstance;
102+
}
89103
}

modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020

2121
import javax.mail.Session;
2222
import javax.mail.internet.MimeMessage;
23+
import javax.net.ssl.SSLSocketFactory;
2324
import java.io.ByteArrayInputStream;
2425
import java.io.File;
2526
import java.io.IOException;
27+
import java.util.AbstractMap;
28+
import java.util.AbstractMap.SimpleEntry;
2629
import java.util.GregorianCalendar;
2730
import java.util.Properties;
2831
import java.util.UUID;
@@ -283,6 +286,50 @@ public void testDKIMPriming()
283286
assertThat(((ImmutableDelegatingSMTPMessage) mimeMessage).getDelegate()).isInstanceOf(DkimMessage.class);
284287
}
285288

289+
@Test
290+
public void testSSLSocketFactoryClassConfig() {
291+
final Mailer mailer = MailerBuilder
292+
.withSMTPServer("host", 25, null, null)
293+
.withCustomSSLFactory("teh_class")
294+
.buildMailer();
295+
296+
final Session session = mailer.getSession();
297+
298+
assertThat(session.getProperties()).contains(new SimpleEntry<String, Object>("mail.smtp.ssl.socketFactory.class", "teh_class"));
299+
assertThat(session.getProperties()).doesNotContainKey("mail.smtp.ssl.socketFactory");
300+
}
301+
302+
@Test
303+
public void testSSLSocketFactoryInstanceConfig() {
304+
final SSLSocketFactory mockFactory = mock(SSLSocketFactory.class);
305+
306+
final Mailer mailer = MailerBuilder
307+
.withSMTPServer("host", 25, null, null)
308+
.withCustomSSLFactoryInstance(mockFactory)
309+
.buildMailer();
310+
311+
final Session session = mailer.getSession();
312+
313+
assertThat(session.getProperties()).contains(new SimpleEntry<String, Object>("mail.smtp.ssl.socketFactory", mockFactory));
314+
assertThat(session.getProperties()).doesNotContainKey("mail.smtp.ssl.socketFactory.class");
315+
}
316+
317+
@Test
318+
public void testSSLSocketFactoryCombinedConfig() {
319+
final SSLSocketFactory mockFactory = mock(SSLSocketFactory.class);
320+
321+
final Mailer mailer = MailerBuilder
322+
.withSMTPServer("host", 25, null, null)
323+
.withCustomSSLFactoryInstance(mockFactory)
324+
.withCustomSSLFactory("teh_class")
325+
.buildMailer();
326+
327+
final Session session = mailer.getSession();
328+
329+
assertThat(session.getProperties()).contains(new SimpleEntry<String, Object>("mail.smtp.ssl.socketFactory", mockFactory));
330+
assertThat(session.getProperties()).doesNotContainKey("mail.smtp.ssl.socketFactory.class");
331+
}
332+
286333
@Test
287334
public void testDKIMPrimingAndSmimeCombo()
288335
throws IOException {

modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/ServerConfigTest.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public void NoArgconstructor_WithoutConfigFile_WithoutHost() {
1717
ConfigLoaderTestHelper.clearConfigProperties();
1818

1919
try {
20-
new ServerConfigImpl(null, null, null, null, null);
20+
new ServerConfigImpl(null, null, null, null, null, null);
2121
fail("IllegalArgumentException expected for host");
2222
} catch (IllegalArgumentException e) {
2323
// ok
@@ -29,7 +29,7 @@ public void NoArgconstructor_WithoutConfigFile_WithoutHost() {
2929
public void NoArgconstructor_WithoutConfigFile_WithoutPort() {
3030
ConfigLoaderTestHelper.clearConfigProperties();
3131
try {
32-
new ServerConfigImpl("host", null, null, null, null);
32+
new ServerConfigImpl("host", null, null, null, null, null);
3333
fail("IllegalArgumentException expected for port");
3434
} catch (IllegalArgumentException e) {
3535
// ok
@@ -39,11 +39,11 @@ public void NoArgconstructor_WithoutConfigFile_WithoutPort() {
3939
@Test
4040
public void NoArgconstructor_WithoutConfigFile_MissingPasswordOrUsername() {
4141
ConfigLoaderTestHelper.clearConfigProperties();
42-
ServerConfig serverConfig = new ServerConfigImpl("host", 1234, "username", null, null);
43-
verifyServerConfig(serverConfig, "host", 1234, "username", null, null);
42+
ServerConfig serverConfig = new ServerConfigImpl("host", 1234, "username", null, null, null);
43+
verifyServerConfig(serverConfig, "host", 1234, "username", null, null, null);
4444

4545
try {
46-
new ServerConfigImpl("host", 1234, null, "password", null);
46+
new ServerConfigImpl("host", 1234, null, "password", null, null);
4747
fail("IllegalArgumentException expected for username");
4848
} catch (IllegalArgumentException e) {
4949
assertThat(e.getMessage()).containsIgnoringCase("username");
@@ -53,27 +53,29 @@ public void NoArgconstructor_WithoutConfigFile_MissingPasswordOrUsername() {
5353
@Test
5454
public void NoArgconstructor_WithoutConfigFile_Authenticated() {
5555
ConfigLoaderTestHelper.clearConfigProperties();
56-
ServerConfig serverConfig = new ServerConfigImpl("host", 1234, "username", "password", null);
57-
verifyServerConfig(serverConfig, "host", 1234, "username", "password", null);
56+
ServerConfig serverConfig = new ServerConfigImpl("host", 1234, "username", "password", null, null);
57+
verifyServerConfig(serverConfig, "host", 1234, "username", "password", null, null);
5858
}
5959

6060
@Test
6161
public void testToString() {
6262
ConfigLoaderTestHelper.clearConfigProperties();
63-
ServerConfig serverConfig = new ServerConfigImpl("host", 1234, null, null, null);
63+
ServerConfig serverConfig = new ServerConfigImpl("host", 1234, null, null, null, null);
6464
assertThat(serverConfig.toString()).isEqualTo("host:1234");
65-
serverConfig = new ServerConfigImpl("host", 1234, "username", null, null);
65+
serverConfig = new ServerConfigImpl("host", 1234, "username", null, null, null);
6666
assertThat(serverConfig.toString()).isEqualTo("host:1234, username: username");
67-
serverConfig = new ServerConfigImpl("host", 1234, "username", "password", null);
67+
serverConfig = new ServerConfigImpl("host", 1234, "username", "password", null, null);
6868
assertThat(serverConfig.toString()).isEqualTo("host:1234, username: username (authenticated)");
6969
}
7070

7171
@SuppressWarnings("SameParameterValue")
72-
private void verifyServerConfig(ServerConfig serverConfig, @Nullable String host, @Nullable Integer port, @Nullable String username, @Nullable String password, Object customSSLFactoryClass) {
72+
private void verifyServerConfig(ServerConfig serverConfig, @Nullable String host, @Nullable Integer port, @Nullable String username, @Nullable String password, Object customSSLFactoryClass,
73+
Object customSSLFactoryInstance) {
7374
assertThat(serverConfig.getHost()).isEqualTo(host);
7475
assertThat(serverConfig.getPort()).isEqualTo(port);
7576
assertThat(serverConfig.getUsername()).isEqualTo(username);
7677
assertThat(serverConfig.getPassword()).isEqualTo(password);
7778
assertThat(serverConfig.getCustomSSLFactoryClass()).isEqualTo(customSSLFactoryClass);
79+
assertThat(serverConfig.getCustomSSLFactoryInstance()).isEqualTo(customSSLFactoryInstance);
7880
}
7981
}

0 commit comments

Comments
 (0)