diff --git a/.github/labeler.yml b/.github/labeler.yml
index 565313449..e242aa680 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -32,6 +32,8 @@
- any-glob-to-any-file:
- spring-cloud-aws-autoconfigure/src/*/java/io/awspring/cloud/autoconfigure/ses/*
- spring-cloud-aws-ses/**/*
+ - spring-cloud-aws-autoconfigure/src/*/java/io/awspring/cloud/autoconfigure/sesv2/*
+ - spring-cloud-aws-sesv2/**/*
"component: sns":
- changed-files:
- any-glob-to-any-file:
diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc
index 40095040c..c3f748ab0 100644
--- a/docs/src/main/asciidoc/_configprops.adoc
+++ b/docs/src/main/asciidoc/_configprops.adoc
@@ -85,6 +85,12 @@
|spring.cloud.aws.ses.from-arn | | Configures from ARN. Only applies to SendRawEmail operation.
|spring.cloud.aws.ses.region | | Overrides the default region.
|spring.cloud.aws.ses.source-arn | | Configures source ARN. Used only for sending authorization.
+|spring.cloud.aws.sesv2.configuration-set-name | | Configures configuration set name.
+|spring.cloud.aws.sesv2.dualstack-enabled | | Configure whether the AWS client should use the AWS dualstack endpoint. Note that not each AWS service supports dual-stack. For complete list check AWS services that support IPv6
+|spring.cloud.aws.sesv2.enabled | `+++true+++` | Enables Simple Email Service V2 integration.
+|spring.cloud.aws.sesv2.endpoint | | Overrides the default endpoint.
+|spring.cloud.aws.sesv2.region | | Overrides the default region.
+|spring.cloud.aws.sesv2.identity-arn | | Configures identity ARN. Used only for sending authorization.
|spring.cloud.aws.sns.dualstack-enabled | | Configure whether the AWS client should use the AWS dualstack endpoint. Note that not each AWS service supports dual-stack. For complete list check AWS services that support IPv6
|spring.cloud.aws.sns.enabled | `+++true+++` | Enables SNS integration.
|spring.cloud.aws.sns.endpoint | | Overrides the default endpoint.
@@ -98,4 +104,4 @@
|spring.cloud.aws.sqs.queue-not-found-strategy | |
|spring.cloud.aws.sqs.region | | Overrides the default region.
-|===
\ No newline at end of file
+|===
diff --git a/docs/src/main/asciidoc/ses.adoc b/docs/src/main/asciidoc/ses.adoc
index 404f325e7..8979a3a45 100644
--- a/docs/src/main/asciidoc/ses.adoc
+++ b/docs/src/main/asciidoc/ses.adoc
@@ -221,3 +221,32 @@ Sample IAM policy granting access to SES:
]
}
----
+
+=== AWS SES API v2
+
+To use AWS SES API v2 instead of v1, use the following dependency instead:
+
+[source,xml]
+----
+
+ io.awspring.cloud
+ spring-cloud-aws-starter-sesv2
+
+----
+
+The associated configuration options are:
+
+[cols="3,3,1,1"]
+|===
+| Name | Description | Required | Default value
+| `spring.cloud.aws.ses2.enabled` | Enables the SES integration. | No | `true`
+| `spring.cloud.aws.ses2.endpoint` | Configures endpoint used by `SesClient`. | No |
+| `spring.cloud.aws.ses2.region` | Configures region used by `SesClient`. | No |
+| `spring.cloud.aws.ses2.identity-arn` | Configures identity ARN, used only for sending authorization. | No |
+| `spring.cloud.aws.ses2.configuration-set-name` | The configuration set name used for every message | No |
+|===
+
+`identityArn` is the ARN of the identity that is associated with the sending authorization policy that permits you to use the email address specified as `from` when sending emails.
+For more information about sending authorization, see the https://docs.aws.amazon.com/ses/latest/dg/sending-authorization.html[Amazon SES Developer Guide].
+
+The `SesV2ClientCustomizer` can be used instead of the `SesClientCustomizer`.
diff --git a/pom.xml b/pom.xml
index a2fb96b73..5e8574596 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,7 @@
spring-cloud-aws-parameter-storespring-cloud-aws-secrets-managerspring-cloud-aws-ses
+ spring-cloud-aws-sesv2spring-cloud-aws-snsspring-cloud-aws-sqsspring-cloud-aws-dynamodb
@@ -52,13 +53,14 @@
spring-cloud-aws-starters/spring-cloud-aws-starter-s3spring-cloud-aws-starters/spring-cloud-aws-starter-secrets-managerspring-cloud-aws-starters/spring-cloud-aws-starter-ses
+ spring-cloud-aws-starters/spring-cloud-aws-starter-sesv2spring-cloud-aws-starters/spring-cloud-aws-starter-snsspring-cloud-aws-starters/spring-cloud-aws-starter-sqsspring-cloud-aws-samplesspring-cloud-aws-testspring-cloud-aws-modulithdocs
-
+
diff --git a/spring-cloud-aws-autoconfigure/pom.xml b/spring-cloud-aws-autoconfigure/pom.xml
index 47334255e..bf128aa91 100644
--- a/spring-cloud-aws-autoconfigure/pom.xml
+++ b/spring-cloud-aws-autoconfigure/pom.xml
@@ -66,6 +66,11 @@
spring-cloud-aws-sestrue
+
+ io.awspring.cloud
+ spring-cloud-aws-sesv2
+ true
+ io.awspring.cloudspring-cloud-aws-sns
diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesAutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesAutoConfiguration.java
new file mode 100644
index 000000000..4f9e31953
--- /dev/null
+++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesAutoConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.autoconfigure.sesv2;
+
+import io.awspring.cloud.autoconfigure.AwsSyncClientCustomizer;
+import io.awspring.cloud.autoconfigure.core.*;
+import io.awspring.cloud.sesv2.SimpleEmailServiceJavaMailSender;
+import io.awspring.cloud.sesv2.SimpleEmailServiceMailSender;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.*;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.mail.MailSender;
+import org.springframework.mail.javamail.JavaMailSender;
+import software.amazon.awssdk.services.sesv2.SesV2Client;
+import software.amazon.awssdk.services.sesv2.SesV2ClientBuilder;
+
+/**
+ * {@link EnableAutoConfiguration} for {@link SimpleEmailServiceMailSender} and
+ * {@link SimpleEmailServiceJavaMailSender}.
+ *
+ * @author Agim Emruli
+ * @author Eddú Meléndez
+ * @author Arun Patra
+ */
+@AutoConfiguration
+@EnableConfigurationProperties(SesProperties.class)
+@ConditionalOnClass({ SesV2Client.class, MailSender.class, SimpleEmailServiceJavaMailSender.class })
+@AutoConfigureAfter({ CredentialsProviderAutoConfiguration.class, RegionProviderAutoConfiguration.class })
+@ConditionalOnProperty(name = "spring.cloud.aws.sesv2.enabled", havingValue = "true", matchIfMissing = true)
+public class SesAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public SesV2Client sesV2Client(SesProperties properties, AwsClientBuilderConfigurer awsClientBuilderConfigurer,
+ ObjectProvider> configurer,
+ ObjectProvider connectionDetails,
+ ObjectProvider sesClientCustomizers,
+ ObjectProvider awsSyncClientCustomizers) {
+ return awsClientBuilderConfigurer.configureSyncClient(SesV2Client.builder(), properties,
+ connectionDetails.getIfAvailable(), configurer.getIfAvailable(), sesClientCustomizers.orderedStream(),
+ awsSyncClientCustomizers.orderedStream()).build();
+ }
+
+ @Bean
+ @ConditionalOnMissingClass("jakarta.mail.Session")
+ @ConditionalOnMissingBean
+ public MailSender simpleMailSender(SesV2Client sesClient, SesProperties properties) {
+ return new SimpleEmailServiceMailSender(sesClient, properties.getIdentityArn(),
+ properties.getConfigurationSetName());
+ }
+
+ @Bean
+ @ConditionalOnClass(name = "jakarta.mail.Session")
+ @ConditionalOnMissingBean
+ public JavaMailSender javaMailSender(SesV2Client sesClient, SesProperties properties) {
+ return new SimpleEmailServiceJavaMailSender(sesClient, properties.getIdentityArn(),
+ properties.getConfigurationSetName());
+ }
+
+}
diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesProperties.java
new file mode 100644
index 000000000..46e1ca971
--- /dev/null
+++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesProperties.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.autoconfigure.sesv2;
+
+import io.awspring.cloud.autoconfigure.AwsClientProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.lang.Nullable;
+
+/**
+ * Properties related to AWS Simple Email Service.
+ *
+ * @author Eddú Meléndez
+ * @author Arun Patra
+ */
+@ConfigurationProperties(prefix = SesProperties.PREFIX)
+public class SesProperties extends AwsClientProperties {
+
+ /**
+ * The prefix used for AWS credentials related properties.
+ */
+ public static final String PREFIX = "spring.cloud.aws.sesv2";
+
+ /**
+ * Configures identity ARN. Used only for sending authorization.
+ */
+ @Nullable
+ private String identityArn;
+
+ /**
+ * Configures configuration set name.
+ */
+ @Nullable
+ private String configurationSetName;
+
+ @Nullable
+ public String getIdentityArn() {
+ return identityArn;
+ }
+
+ @Nullable
+ public String getConfigurationSetName() {
+ return configurationSetName;
+ }
+
+ public void setIdentityArn(@Nullable String identityArn) {
+ this.identityArn = identityArn;
+ }
+
+ public void setConfigurationSetName(@Nullable String configurationSetName) {
+ this.configurationSetName = configurationSetName;
+ }
+}
diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesV2ClientCustomizer.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesV2ClientCustomizer.java
new file mode 100644
index 000000000..10899ea87
--- /dev/null
+++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/SesV2ClientCustomizer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.autoconfigure.sesv2;
+
+import io.awspring.cloud.autoconfigure.AwsClientCustomizer;
+import software.amazon.awssdk.services.sesv2.SesV2ClientBuilder;
+
+/**
+ * Callback interface that can be used to customize a {@link SesV2ClientBuilder}.
+ *
+ * @author Maciej Walkowiak
+ * @since 3.3.0
+ */
+@FunctionalInterface
+public interface SesV2ClientCustomizer extends AwsClientCustomizer {
+}
diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/package-info.java
new file mode 100644
index 000000000..6526e155b
--- /dev/null
+++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sesv2/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Auto-configuration for Amazon SES (Simple Email Service) integrations.
+ */
+@org.springframework.lang.NonNullApi
+@org.springframework.lang.NonNullFields
+package io.awspring.cloud.autoconfigure.sesv2;
diff --git a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 13c7a0de5..749936bad 100644
--- a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -12,6 +12,12 @@
"description": "Enables Simple Email Service integration.",
"type": "java.lang.Boolean"
},
+ {
+ "defaultValue": true,
+ "name": "spring.cloud.aws.sesv2.enabled",
+ "description": "Enables Simple Email Service integration.",
+ "type": "java.lang.Boolean"
+ },
{
"defaultValue": true,
"name": "spring.cloud.aws.s3.enabled",
diff --git a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 657cbf245..fb978ed25 100644
--- a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -4,6 +4,7 @@ io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration
io.awspring.cloud.autoconfigure.imds.ImdsAutoConfiguration
io.awspring.cloud.autoconfigure.metrics.CloudWatchExportAutoConfiguration
io.awspring.cloud.autoconfigure.ses.SesAutoConfiguration
+io.awspring.cloud.autoconfigure.sesv2.SesAutoConfiguration
io.awspring.cloud.autoconfigure.s3.S3TransferManagerAutoConfiguration
io.awspring.cloud.autoconfigure.s3.S3AutoConfiguration
io.awspring.cloud.autoconfigure.s3.S3CrtAsyncClientAutoConfiguration
diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sesv2/SesAutoConfigurationTest.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sesv2/SesAutoConfigurationTest.java
new file mode 100644
index 000000000..6eb1707da
--- /dev/null
+++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sesv2/SesAutoConfigurationTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.autoconfigure.sesv2;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.awspring.cloud.autoconfigure.ConfiguredAwsClient;
+import io.awspring.cloud.autoconfigure.core.AwsAutoConfiguration;
+import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
+import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
+import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
+import java.net.URI;
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.mail.MailSender;
+import org.springframework.mail.javamail.JavaMailSender;
+import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.apache.ApacheHttpClient;
+import software.amazon.awssdk.services.sesv2.SesV2Client;
+import software.amazon.awssdk.services.sesv2.SesV2ClientBuilder;
+
+/**
+ * Tests for class {@link io.awspring.cloud.autoconfigure.ses.SesAutoConfiguration}.
+ *
+ * @author Eddú Meléndez
+ * @author Maciej Walkowiak
+ * @author Arun Patra
+ */
+class SesAutoConfigurationTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.cloud.aws.region.static:eu-west-1")
+ .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class,
+ CredentialsProviderAutoConfiguration.class, SesAutoConfiguration.class));
+
+ @Test
+ void mailSenderWithJavaMail() {
+ this.contextRunner.run(context -> {
+ assertThat(context).hasSingleBean(MailSender.class);
+ assertThat(context).hasSingleBean(JavaMailSender.class);
+ assertThat(context).getBean(JavaMailSender.class).isSameAs(context.getBean(MailSender.class));
+ });
+ }
+
+ @Test
+ void mailSenderWithoutSesV2ClientInTheClasspath() {
+ this.contextRunner.withClassLoader(new FilteredClassLoader(SesV2Client.class)).run(context -> {
+ assertThat(context).doesNotHaveBean(MailSender.class);
+ assertThat(context).doesNotHaveBean(JavaMailSender.class);
+ });
+ }
+
+ @Test
+ void mailSenderWithSimpleEmail() {
+ this.contextRunner.withClassLoader(new FilteredClassLoader(jakarta.mail.Session.class)).run(context -> {
+ assertThat(context).hasSingleBean(MailSender.class);
+ assertThat(context).getBean("simpleMailSender").isNotNull().isSameAs(context.getBean(MailSender.class));
+ });
+ }
+
+ @Test
+ void sesAutoConfigurationIsDisabled() {
+ this.contextRunner.withPropertyValues("spring.cloud.aws.sesv2.enabled:false").run(context -> {
+ assertThat(context).doesNotHaveBean(MailSender.class);
+ assertThat(context).doesNotHaveBean(JavaMailSender.class);
+ });
+ }
+
+ @Test
+ void withCustomEndpoint() {
+ this.contextRunner.withPropertyValues("spring.cloud.aws.sesv2.endpoint:http://localhost:8090").run(context -> {
+ ConfiguredAwsClient client = new ConfiguredAwsClient(context.getBean(SesV2Client.class));
+ assertThat(client.getEndpoint()).isEqualTo(URI.create("http://localhost:8090"));
+ assertThat(client.isEndpointOverridden()).isTrue();
+ });
+ }
+
+ @Test
+ void withCustomGlobalEndpoint() {
+ this.contextRunner.withPropertyValues("spring.cloud.aws.endpoint:http://localhost:8090").run(context -> {
+ ConfiguredAwsClient client = new ConfiguredAwsClient(context.getBean(SesV2Client.class));
+ assertThat(client.getEndpoint()).isEqualTo(URI.create("http://localhost:8090"));
+ assertThat(client.isEndpointOverridden()).isTrue();
+ });
+ }
+
+ @Test
+ void withCustomGlobalEndpointAndSesEndpoint() {
+ this.contextRunner.withPropertyValues("spring.cloud.aws.endpoint:http://localhost:8090",
+ "spring.cloud.aws.sesv2.endpoint:http://localhost:9999").run(context -> {
+ ConfiguredAwsClient client = new ConfiguredAwsClient(context.getBean(SesV2Client.class));
+ assertThat(client.getEndpoint()).isEqualTo(URI.create("http://localhost:9999"));
+ assertThat(client.isEndpointOverridden()).isTrue();
+ });
+ }
+
+ @Test
+ void customSesV2ClientConfigurer() {
+ this.contextRunner.withUserConfiguration(CustomAwsClientConfig.class).run(context -> {
+ ConfiguredAwsClient sesClient = new ConfiguredAwsClient(context.getBean(SesV2Client.class));
+ assertThat(sesClient.getApiCallTimeout()).isEqualTo(Duration.ofMillis(2000));
+ assertThat(sesClient.getSyncHttpClient()).isNotNull();
+ });
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ static class CustomAwsClientConfig {
+
+ @Bean
+ AwsClientCustomizer snsClientBuilderAwsClientConfigurer() {
+ return new SesAwsClientConfigurer();
+ }
+
+ static class SesAwsClientConfigurer implements AwsClientCustomizer {
+ @Override
+ public ClientOverrideConfiguration overrideConfiguration() {
+ return ClientOverrideConfiguration.builder().apiCallTimeout(Duration.ofMillis(2000)).build();
+ }
+
+ @Override
+ public SdkHttpClient httpClient() {
+ return ApacheHttpClient.builder().connectionTimeout(Duration.ofMillis(1542)).build();
+ }
+ }
+
+ }
+
+}
diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sesv2/SesV2ClientCustomizerTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sesv2/SesV2ClientCustomizerTests.java
new file mode 100644
index 000000000..4f8a00851
--- /dev/null
+++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sesv2/SesV2ClientCustomizerTests.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.autoconfigure.sesv2;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.awspring.cloud.autoconfigure.AwsSyncClientCustomizer;
+import io.awspring.cloud.autoconfigure.ConfiguredAwsClient;
+import io.awspring.cloud.autoconfigure.core.AwsAutoConfiguration;
+import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
+import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
+import io.awspring.cloud.autoconfigure.ses.SesAutoConfiguration;
+import io.awspring.cloud.autoconfigure.ses.SesClientCustomizer;
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import software.amazon.awssdk.http.apache.ApacheHttpClient;
+import software.amazon.awssdk.services.ses.SesClient;
+
+/**
+ * Tests for {@link SesClientCustomizer}.
+ *
+ * @author Maciej Walkowiak
+ */
+class SesV2ClientCustomizerTests {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.cloud.aws.region.static:eu-west-1",
+ "spring.cloud.aws.credentials.access-key:noop", "spring.cloud.aws.credentials.secret-key:noop")
+ .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class,
+ CredentialsProviderAutoConfiguration.class, SesAutoConfiguration.class));
+
+ @Test
+ void customClientCustomizer() {
+ contextRunner.withUserConfiguration(CustomizerConfig.class).run(context -> {
+ ConfiguredAwsClient client = new ConfiguredAwsClient(context.getBean(SesClient.class));
+ assertThat(client.getApiCallTimeout()).describedAs("sets property from first customizer")
+ .isEqualTo(Duration.ofMillis(2001));
+ assertThat(client.getApiCallAttemptTimeout()).describedAs("sets property from second customizer")
+ .isEqualTo(Duration.ofMillis(2002));
+ assertThat(client.getSyncHttpClient()).describedAs("sets property from common client customizer")
+ .isNotNull();
+ });
+ }
+
+ @Test
+ void customClientCustomizerWithOrder() {
+ contextRunner.withUserConfiguration(CustomizerConfigWithOrder.class).run(context -> {
+ ConfiguredAwsClient client = new ConfiguredAwsClient(context.getBean(SesClient.class));
+ assertThat(client.getApiCallTimeout())
+ .describedAs("property from the customizer with higher order takes precedence")
+ .isEqualTo(Duration.ofMillis(2001));
+ });
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ static class CustomizerConfig {
+
+ @Bean
+ SesClientCustomizer customizer() {
+ return builder -> {
+ builder.overrideConfiguration(builder.overrideConfiguration().copy(c -> {
+ c.apiCallTimeout(Duration.ofMillis(2001));
+ }));
+ };
+ }
+
+ @Bean
+ SesClientCustomizer customizer2() {
+ return builder -> {
+ builder.overrideConfiguration(builder.overrideConfiguration().copy(c -> {
+ c.apiCallAttemptTimeout(Duration.ofMillis(2002));
+ }));
+ };
+ }
+
+ @Bean
+ AwsSyncClientCustomizer awsSyncClientCustomizer() {
+ return builder -> {
+ builder.httpClient(ApacheHttpClient.builder().connectionTimeout(Duration.ofMillis(1542)).build());
+ };
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ static class CustomizerConfigWithOrder {
+
+ @Bean
+ @Order(2)
+ SesClientCustomizer customizer() {
+ return builder -> {
+ builder.overrideConfiguration(builder.overrideConfiguration().copy(c -> {
+ c.apiCallTimeout(Duration.ofMillis(2001));
+ }));
+ };
+ }
+
+ @Bean
+ @Order(1)
+ SesClientCustomizer customizer2() {
+ return builder -> {
+ builder.overrideConfiguration(builder.overrideConfiguration().copy(c -> {
+ c.apiCallTimeout(Duration.ofMillis(2000));
+ }));
+ };
+ }
+ }
+
+}
diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml
index 4ef833ef7..acd13f368 100644
--- a/spring-cloud-aws-dependencies/pom.xml
+++ b/spring-cloud-aws-dependencies/pom.xml
@@ -132,6 +132,12 @@
${project.version}
+
+ io.awspring.cloud
+ spring-cloud-aws-sesv2
+ ${project.version}
+
+
io.awspring.cloudspring-cloud-aws-s3
@@ -192,6 +198,12 @@
${project.version}
+
+ io.awspring.cloud
+ spring-cloud-aws-starter-sesv2
+ ${project.version}
+
+
io.awspring.cloudspring-cloud-aws-starter-s3
diff --git a/spring-cloud-aws-samples/pom.xml b/spring-cloud-aws-samples/pom.xml
index d6a00fe4f..2814ada85 100644
--- a/spring-cloud-aws-samples/pom.xml
+++ b/spring-cloud-aws-samples/pom.xml
@@ -21,6 +21,7 @@
spring-cloud-aws-s3-samplespring-cloud-aws-secrets-manager-samplespring-cloud-aws-ses-sample
+ spring-cloud-aws-sesv2-samplespring-cloud-aws-sns-samplespring-cloud-aws-sqs-sample
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/docker-compose.yml b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/docker-compose.yml
new file mode 100644
index 000000000..5fb0086c0
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/docker-compose.yml
@@ -0,0 +1,20 @@
+version: '3.8'
+
+services:
+ sesv2-sample-localstack:
+ container_name: localstack
+ environment:
+ - DEBUG=1
+ - LOCALSTACK_HOSTNAME=localhost
+ - TEST_AWS_ACCOUNT_ID=000000000000
+ - AWS_DEFAULT_REGION=us-east-1
+ - SERVICES=sesv2
+ - S3_MOUNT=/tmp
+ - LOCALSTACK_AUTH_TOKEN=
+ # SES V2 is only available in the pro version. See https://docs.localstack.cloud/references/coverage/coverage_sesv2/
+ image: localstack/localstack-pro:latest
+ ports:
+ - "4566:4566"
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/pom.xml b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/pom.xml
new file mode 100644
index 000000000..52a4b1b5e
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ io.awspring.cloud
+ spring-cloud-aws-samples
+ 3.4.0-SNAPSHOT
+
+ 4.0.0
+
+ spring-cloud-aws-sesv2-sample
+ Spring Cloud AWS SES V2 Sample
+
+
+
+ io.awspring.cloud
+ spring-cloud-aws-starter-sesv2
+
+
+
+ jakarta.mail
+ jakarta.mail-api
+
+
+
+ org.eclipse.angus
+ jakarta.mail
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/java/io/awspring/cloud/samples/sesv2/MailSendingApplication.java b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/java/io/awspring/cloud/samples/sesv2/MailSendingApplication.java
new file mode 100644
index 000000000..fc3802f0a
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/java/io/awspring/cloud/samples/sesv2/MailSendingApplication.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.samples.sesv2;
+
+import java.io.File;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.mail.MailSender;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import software.amazon.awssdk.services.sesv2.SesV2Client;
+
+/**
+ * A sample application to demonstrate sending simple emails and emails with attachments.
+ *
+ * To run this sample application, you need to do either of the following.
+ *
+ *
If you wish to send emails to a real email ID, you need to verify identities as mentioned in
+ * the Amazon Simple Email Service
+ * docs. After you do that, simply update this sample app with email IDs that you have verified with AWS.
+ *
If you wish to just test out email sending capability in a test environment, you can do so by running localstack.
+ * Just issue the following command from the root of the `spring-cloud-aws-ses-sample`:
+ *
+ *
+ * docker-compose -f docker-compose.yml up -d
+ *
+ *
+ * See more information on localstack see here and
+ * here.
+ *
+ *
+ *
+ */
+@SpringBootApplication
+public class MailSendingApplication {
+ private static final String SENDER = "something@foo.bar";
+ private static final String RECIPIENT = "someMail@foo.bar";
+
+ public static void main(String[] args) {
+ SpringApplication.run(MailSendingApplication.class, args);
+ }
+
+ @Bean
+ ApplicationRunner applicationRunner(MailSender mailSender, SesV2Client sesClient) {
+ return args -> {
+ sendAnEmail(mailSender, sesClient);
+ sendAnEmailWithAttachment(mailSender, sesClient);
+ sendHtmlEmail(mailSender, sesClient);
+ // check localstack logs for sent email, if you use localstack for running this sample
+ };
+ }
+
+ public static void sendAnEmail(MailSender mailSender, SesV2Client sesClient) {
+ // e-mail address has to verified before we email it. If it is not verified SES will return error.
+ sesClient.createEmailIdentity(builder -> builder.emailIdentity(RECIPIENT).build());
+ sesClient.createEmailIdentity(builder -> builder.emailIdentity(SENDER).build());
+
+ // SimpleMailMessage is created, and we use MailSender bean which is autoconfigured to send an email through
+ // SES.
+ SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
+ simpleMailMessage.setFrom(SENDER);
+ simpleMailMessage.setTo(RECIPIENT);
+ simpleMailMessage.setSubject("test subject");
+ simpleMailMessage.setText("test content");
+ mailSender.send(simpleMailMessage);
+ }
+
+ /**
+ * To send emails with attachments, you must provide the Java Mail API and an implementation of the API in the
+ * classpath. See the dependencies provided in this sample app. If you don't provider an implementation of the Java
+ * Mail API, you would get the following exception at runtime.
+ *
+ *
+ * java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
+ *
+ *
+ * @param mailSender A {@link JavaMailSender}.
+ * @param sesClient An {@link SesV2Client}.
+ */
+ public static void sendAnEmailWithAttachment(MailSender mailSender, SesV2Client sesClient) {
+ // e-mail address has to verified before we email it. If it is not verified SES will return error.
+ sesClient.createEmailIdentity(builder -> builder.emailIdentity(RECIPIENT).build());
+ sesClient.createEmailIdentity(builder -> builder.emailIdentity(SENDER).build());
+
+ // A JavaMailSender is needed. Spring Cloud AWS SES automatically configures a JavaMailSender when it finds
+ // the Java Mail API in the classpath. At runtime, an implementation of teh Java Mail API must also be
+ // available.
+ JavaMailSender javaMailSender = (JavaMailSender) mailSender;
+ javaMailSender.send(mimeMessage -> {
+ MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
+ helper.addTo(RECIPIENT);
+ helper.setFrom(SENDER);
+ File resource = new ClassPathResource("answer.txt").getFile();
+ helper.addAttachment("answer.txt", resource.getAbsoluteFile());
+ helper.setSubject("What is the meaning of life, the universe, and everything?");
+ helper.setText("Open the attached file for the answer you are seeking", false);
+ });
+ }
+
+ /**
+ * To send HTML emails, you must provide the Java Mail API and an implementation of the API in the classpath. See
+ * the dependencies provided in this sample app. If you don't provider an implementation of the Java Mail API, you
+ * would get the following exception at runtime.
+ *
+ *
+ * java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
+ *
+ *
+ * @param mailSender A {@link JavaMailSender}.
+ * @param sesClient An {@link SesV2Client}.
+ */
+ public static void sendHtmlEmail(MailSender mailSender, SesV2Client sesClient) {
+ // e-mail address has to verified before we email it. If it is not verified SES will return error.
+ sesClient.createEmailIdentity(builder -> builder.emailIdentity(RECIPIENT).build());
+ sesClient.createEmailIdentity(builder -> builder.emailIdentity(SENDER).build());
+
+ // A JavaMailSender is needed. Spring Cloud AWS SES automatically configures a JavaMailSender when it finds
+ // the Java Mail API in the classpath. At runtime, an implementation of the Java Mail API must also be
+ // available.
+ JavaMailSender javaMailSender = (JavaMailSender) mailSender;
+ javaMailSender.send(mimeMessage -> {
+ MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
+ helper.addTo(RECIPIENT);
+ helper.setFrom(SENDER);
+ helper.setSubject("What is the meaning of life, the universe, and everything?");
+ String htmlMessage = """
+
What is the meaning of life, the universe, and everything?
+
42
+ """;
+ helper.setText(htmlMessage, true);
+ });
+ }
+}
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/resources/answer.txt b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/resources/answer.txt
new file mode 100644
index 000000000..2980103fe
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/resources/answer.txt
@@ -0,0 +1,2 @@
+Question to Spring Cloud AWS: What is the meaning of life, the universe, and everything?
+Answer from Spring Cloud AWS: 42
diff --git a/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/resources/application.properties b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/resources/application.properties
new file mode 100644
index 000000000..f5935aa2b
--- /dev/null
+++ b/spring-cloud-aws-samples/spring-cloud-aws-sesv2-sample/src/main/resources/application.properties
@@ -0,0 +1,7 @@
+# LocalStack configuration
+spring.cloud.aws.endpoint=http://localhost:4566
+spring.cloud.aws.region.static=us-east-1
+spring.cloud.aws.credentials.access-key=noop
+spring.cloud.aws.credentials.secret-key=noop
+spring.cloud.aws.sesv2.enabled=true
+spring.cloud.aws.ses.enabled=false
diff --git a/spring-cloud-aws-sesv2/pom.xml b/spring-cloud-aws-sesv2/pom.xml
new file mode 100644
index 000000000..32287919e
--- /dev/null
+++ b/spring-cloud-aws-sesv2/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ io.awspring.cloud
+ spring-cloud-aws
+ 3.4.0-SNAPSHOT
+
+ 4.0.0
+
+ spring-cloud-aws-sesv2
+ Spring Cloud AWS SES V2 Integration
+
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework
+ spring-context-support
+
+
+ software.amazon.awssdk
+ sesv2
+
+
+ jakarta.mail
+ jakarta.mail-api
+ true
+
+
+ jakarta.activation
+ jakarta.activation-api
+ true
+
+
+ org.eclipse.angus
+ jakarta.mail
+ test
+
+
+
diff --git a/spring-cloud-aws-sesv2/src/main/java/io/awspring/cloud/sesv2/SimpleEmailServiceJavaMailSender.java b/spring-cloud-aws-sesv2/src/main/java/io/awspring/cloud/sesv2/SimpleEmailServiceJavaMailSender.java
new file mode 100644
index 000000000..ae7114f38
--- /dev/null
+++ b/spring-cloud-aws-sesv2/src/main/java/io/awspring/cloud/sesv2/SimpleEmailServiceJavaMailSender.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2013-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.awspring.cloud.sesv2;
+
+import jakarta.activation.FileTypeMap;
+import jakarta.mail.MessagingException;
+import jakarta.mail.Session;
+import jakarta.mail.internet.MimeMessage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.lang.Nullable;
+import org.springframework.mail.MailException;
+import org.springframework.mail.MailParseException;
+import org.springframework.mail.MailPreparationException;
+import org.springframework.mail.MailSendException;
+import org.springframework.mail.javamail.ConfigurableMimeFileTypeMap;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.mail.javamail.MimeMessagePreparator;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import software.amazon.awssdk.core.SdkBytes;
+import software.amazon.awssdk.services.sesv2.SesV2Client;
+import software.amazon.awssdk.services.sesv2.model.RawMessage;
+import software.amazon.awssdk.services.sesv2.model.SendEmailRequest;
+import software.amazon.awssdk.services.sesv2.model.SendEmailResponse;
+
+/**
+ * {@link JavaMailSender} implementation that allows to send {@link MimeMessage} using the Simple E-Mail Service. In
+ * contrast to {@link SimpleEmailServiceMailSender} this class also allows the use of attachment and other mime parts
+ * inside mail messages.
+ *
+ * @author Agim Emruli
+ * @author Eddú Meléndez
+ * @author Arun Patra
+ * @since 1.0
+ */
+public class SimpleEmailServiceJavaMailSender extends SimpleEmailServiceMailSender implements JavaMailSender {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SimpleEmailServiceJavaMailSender.class);
+
+ private static final String SMART_MIME_MESSAGE_CLASS_NAME = "org.springframework.mail.javamail.SmartMimeMessage";
+
+ private Properties javaMailProperties = new Properties();
+
+ @Nullable
+ private volatile Session session;
+
+ @Nullable
+ private String defaultEncoding;
+
+ @Nullable
+ private FileTypeMap defaultFileTypeMap;
+
+ public SimpleEmailServiceJavaMailSender(SesV2Client sesClient) {
+ this(sesClient, null);
+ }
+
+ public SimpleEmailServiceJavaMailSender(SesV2Client sesClient, @Nullable String sourceArn) {
+ this(sesClient, sourceArn, null);
+ }
+
+ public SimpleEmailServiceJavaMailSender(SesV2Client sesClient, @Nullable String sourceArn,
+ @Nullable String configurationSetName) {
+ super(sesClient, sourceArn, configurationSetName);
+ }
+
+ /**
+ * Allow Map access to the JavaMail properties of this sender, with the option to add or override specific entries.
+ *
+ * Useful for specifying entries directly, for example via "javaMailProperties[mail.from]".
+ *
+ * @return java mail properties
+ */
+ protected Properties getJavaMailProperties() {
+ return this.javaMailProperties;
+ }
+
+ /**
+ * Set JavaMail properties for the {@code Session}.
+ *
+ * A new {@code Session} will be created with those properties.
+ *
+ * Non-default properties in this instance will override given JavaMail properties.
+ *
+ * @param javaMailProperties java mail props
+ */
+ public void setJavaMailProperties(Properties javaMailProperties) {
+ Assert.notNull(javaMailProperties, "javaMailProperties are required");
+ this.javaMailProperties = javaMailProperties;
+ this.session = null;
+ }
+
+ /**
+ * Return the JavaMail {@code Session}, lazily initializing it if hasn't been specified explicitly.
+ *
+ * @return cached session or a new one from java mail properties
+ */
+ @Nullable
+ protected Session getSession() {
+ if (this.session == null) {
+ this.session = Session.getInstance(getJavaMailProperties());
+ }
+ return this.session;
+ }
+
+ /**
+ * Set the JavaMail {@code Session}, possibly pulled from JNDI.
+ *
+ * Default is a new {@code Session} without defaults, that is completely configured via this instance's properties.
+ *
+ * If using a pre-configured {@code Session}, non-default properties in this instance will override the settings in
+ * the {@code Session}.
+ *
+ * @param session JavaMail session
+ * @see #setJavaMailProperties
+ */
+ public void setSession(Session session) {
+ Assert.notNull(session, "Session must not be null");
+ this.session = session;
+ }
+
+ /**
+ * Set the default encoding to use for {@link MimeMessage MimeMessages} created by this instance.
+ *
+ * Such an encoding will be auto-detected by {@link MimeMessageHelper}.
+ *
+ * @param defaultEncoding default encoding for mime messages
+ */
+ public void setDefaultEncoding(String defaultEncoding) {
+ this.defaultEncoding = defaultEncoding;
+ }
+
+ /**
+ * Set the default Java Activation {@link FileTypeMap} to use for {@link MimeMessage MimeMessages} created by this
+ * instance.
+ *
+ * A {@code FileTypeMap} specified here will be autodetected by {@link MimeMessageHelper}, avoiding the need to
+ * specify the {@code FileTypeMap} for each {@code MimeMessageHelper} instance.
+ *
+ * For example, you can specify a custom instance of Spring's {@link ConfigurableMimeFileTypeMap} here. If not
+ * explicitly specified, a default {@code ConfigurableMimeFileTypeMap} will be used, containing an extended set of
+ * MIME type mappings (as defined by the {@code mime.types} file contained in the Spring jar).
+ *
+ * @param defaultFileTypeMap Java Activation file type map
+ * @see MimeMessageHelper#setFileTypeMap
+ */
+ public void setDefaultFileTypeMap(FileTypeMap defaultFileTypeMap) {
+ this.defaultFileTypeMap = defaultFileTypeMap;
+ }
+
+ @Override
+ public MimeMessage createMimeMessage() {
+
+ // We have to use reflection as SmartMimeMessage is not package-private
+ if (ClassUtils.isPresent(SMART_MIME_MESSAGE_CLASS_NAME, ClassUtils.getDefaultClassLoader())) {
+ Class> smartMimeMessage = ClassUtils.resolveClassName(SMART_MIME_MESSAGE_CLASS_NAME,
+ ClassUtils.getDefaultClassLoader());
+ Constructor> constructor = ClassUtils.getConstructorIfAvailable(smartMimeMessage, Session.class,
+ String.class, FileTypeMap.class);
+ if (constructor != null) {
+ Object mimeMessage = BeanUtils.instantiateClass(constructor, getSession(), this.defaultEncoding,
+ this.defaultFileTypeMap);
+ return (MimeMessage) mimeMessage;
+ }
+ }
+
+ return new MimeMessage(getSession());
+ }
+
+ @Override
+ public MimeMessage createMimeMessage(InputStream contentStream) throws MailException {
+ Assert.notNull(contentStream, "contentStream are required");
+ try {
+ return new MimeMessage(getSession(), contentStream);
+ }
+ catch (MessagingException e) {
+ throw new MailParseException("Could not parse raw MIME content", e);
+ }
+ }
+
+ @Override
+ public void send(MimeMessage mimeMessage) throws MailException {
+ Assert.notNull(mimeMessage, "mimeMessage are required");
+ this.send(new MimeMessage[] { mimeMessage });
+ }
+
+ @SuppressWarnings("OverloadedVarargsMethod")
+ @Override
+ public void send(MimeMessage... mimeMessages) throws MailException {
+ Assert.notNull(mimeMessages, "mimeMessages are required");
+ Map