+++ /dev/null
-<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/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>de.spring.emails</groupId>
- <artifactId>emails-spring</artifactId>
- <version>1.0-SNAPSHOT</version>
- <name>emails-spring</name>
- <url>http://gumartinm.name</url>
- <description>Sending emails with Spring Framework</description>
- <organization>
- <name>Gustavo Martin Morcuende</name>
- <url>http://www.gumartinm.name</url>
- </organization>
- <scm>
- <developerConnection>scm:git:http://git.gumartinm.name/SpringJava/Emails</developerConnection>
- <url>http://git.gumartinm.name/SpringJava/Emails</url>
- </scm>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <spring.version>4.2.5.RELEASE</spring.version>
- </properties>
-
- <profiles>
- <profile>
- <id>release</id>
- <properties>
- <environment.profile>release</environment.profile>
- </properties>
- <activation>
- <activeByDefault>true</activeByDefault>
- </activation>
- </profile>
- </profiles>
-
- <dependencies>
- <!-- 1/3 Required dependency for log4j 2 with slf4j: binding between log4j
- 2 and slf4j -->
- <dependency>
- <groupId>org.apache.logging.log4j</groupId>
- <artifactId>log4j-slf4j-impl</artifactId>
- <version>2.3</version>
- </dependency>
- <!-- 2/3 Required dependency for log4j 2 with slf4j: log4j 2 maven plugin
- (it is the log4j 2 implementation) -->
- <dependency>
- <groupId>org.apache.logging.log4j</groupId>
- <artifactId>log4j-core</artifactId>
- <version>2.3</version>
- </dependency>
- <!-- 3/3 Required dependency for getting rid of commons logging. This is
- the BRIDGE (no binding) between Jakarta Commons Logging (used by Spring)
- and whatever I am using for logging (in this case I am using log4j 2) See:
- http://www.slf4j.org/legacy.html We need exclusions in every dependency using
- Jakarta Commons Logging (see Spring dependencies below) -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
- <version>1.7.12</version>
- </dependency>
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib</artifactId>
- <version>2.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>${spring.version}</version>
- <!-- Required dependency for getting rid of commons logging and use my
- own logging library (in my case I decided to use log4j 2 under slf4j) -->
- <exclusions>
- <exclusion>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <!-- Required dependency for VelocityEngineUtils -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context-support</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.velocity</groupId>
- <artifactId>velocity</artifactId>
- <version>1.7</version>
- </dependency>
- <dependency>
- <groupId>velocity-tools</groupId>
- <artifactId>velocity-tools</artifactId>
- <version>2.0-beta1</version>
- </dependency>
-
- <!-- Required dependencies for SMTP client -->
- <dependency>
- <groupId>javax.mail</groupId>
- <artifactId>mail</artifactId>
- <version>1.5.0-b01</version>
- </dependency>
- <dependency>
- <groupId>javax.activation</groupId>
- <artifactId>activation</artifactId>
- <version>1.1.1</version>
- </dependency>
-
- <!-- Unitary and integration tests -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-test</artifactId>
- <version>${spring.version}</version>
- <scope>test</scope>
- <!-- Required dependency for getting rid of commons logging and use my
- own logging library (in my case I decided to use log4j 2 under slf4j) -->
- <exclusions>
- <exclusion>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>2.0.43-beta</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
-
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>2.19.1</version>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-failsafe-plugin</artifactId>
- <version>2.19.1</version>
- </plugin>
- </plugins>
- </pluginManagement>
-
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.5.1</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- <encoding>${project.build.sourceEncoding}</encoding>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-resources-plugin</artifactId>
- <version>2.7</version>
- <configuration>
- <encoding>${project.build.sourceEncoding}</encoding>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <version>2.6</version>
- <configuration>
- <archive>
- <manifestEntries>
- <Specification-Title>${project.description}</Specification-Title>
- <Specification-Version>${project.version}</Specification-Version>
- <Specification-Vendor>${project.organization.name}</Specification-Vendor>
- <Implementation-Title>${project.description}</Implementation-Title>
- <Implementation-Version>${project.version}</Implementation-Version>
- <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
- </manifestEntries>
- </archive>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <excludes>
- <exclude>**/*IntegrationTest.java</exclude>
- </excludes>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-failsafe-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>integration-test</goal>
- <goal>verify</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <includes>
- <include>**/*IntegrationTest.java</include>
- </includes>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
--- /dev/null
+<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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>de.spring.emails</groupId>
+ <artifactId>spring-emails</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>spring-emails-web-client</artifactId>
+ <packaging>war</packaging>
+ <name>spring-emails-web-client</name>
+ <url>http://gumartinm.name</url>
+ <description>Emails with Spring Framework. Web Client.</description>
+ <organization>
+ <name>Gustavo Martin Morcuende</name>
+ <url>http://www.gumartinm.name</url>
+ </organization>
+ <scm>
+ <developerConnection>scm:git:http://git.gumartinm.name/JavaForFun</developerConnection>
+ <url>http://git.gumartinm.name/JavaForFun</url>
+ </scm>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-oxm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>velocity-tools</groupId>
+ <artifactId>velocity-tools</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ </dependency>
+
+ <!-- Required by spring-webmvc -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Jackson JSON Processor, required by spring-webmvc. See messageConverters
+ in rest-config.xml -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+
+ <!-- Required by spring-context for using JSR-303. See LocalValidatorFactoryBean
+ in rest-config.xml -->
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ </dependency>
+
+
+ <!-- Unitary and integration tests -->
+ <dependency>
+ <groupId>com.icegreen</groupId>
+ <artifactId>greenmail</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <finalName>${project.artifactId}</finalName>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/webapp</directory>
+ <excludes>
+ <exclude>**/*.*</exclude>
+ </excludes>
+ </resource>
+ <resource>
+ <directory>${basedir}/src/main/resources/</directory>
+ <includes>
+ <include>**/*.*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <webResources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/webapp</directory>
+ <includes>
+ <include>WEB-INF/web.xml</include>
+ </includes>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+package de.spring.emails.services;
+
+import java.util.Locale;
+import java.util.Map;
+
+public interface EmailMakerService {
+
+ public String emailMaker(Map<String, String> text, String templateLocation, Locale locale);
+
+ public String getSubject(String code, Locale locale, Object... args);
+}
--- /dev/null
+package de.spring.emails.services;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.springframework.core.io.Resource;
+
+public interface EmailService {
+
+ /**
+ * This method sends mails.
+ *
+ * @param to the email destination. Required parameter.
+ * @param subject the email subject. Required parameter.
+ * @param text the email text. Required parameter.
+ * @param isHtml if true email is HTML type, otherwise false.
+ * @param attachments file attachments. Optional parameter.
+ * @param inline inline content in mail. Optional parameter.
+ * @throws MessagingException in case of any error.
+ */
+ public void sendEmail(String[] to, String subject, String text, boolean isHtml,
+ List<Resource> attachments, Map<String, Resource> inline) throws MessagingException;
+
+ /**
+ * This method sends mails. It is asynchronous, what means, this method always returns before sending the email.
+ *
+ * @param to the email destination. Required parameter.
+ * @param subject the email subject. Required parameter.
+ * @param text the email text. Required parameter.
+ * @param isHtml if true email is HTML type, otherwise false.
+ * @param attachments file attachments. Optional parameters.
+ * @param inline inline content in mail. Optional parameter.
+ * @throws MessagingException in case of any error.
+ */
+ public void sendEmailAsync(String[] to, String subject, String text, boolean isHtml,
+ List<Resource> attachments, Map<String, Resource> inline) throws MessagingException;
+}
\ No newline at end of file
--- /dev/null
+package de.spring.emails.services.impl;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.stereotype.Service;
+import org.springframework.ui.velocity.VelocityEngineUtils;
+
+import de.spring.emails.services.EmailMakerService;
+
+
+@Service("emailMakerService")
+public class EmailMakerServiceImpl implements EmailMakerService {
+ private static final String TEMPLATES_DEFAULT_EXTENSION = ".vm";
+ private static final String TEMPLATES_DEFAULT_PATH = "email/";
+ private static final String EMAIL_CONTENT_ENCODING = "UTF-8";
+
+ private final VelocityEngine velocityEngine;
+ private final MessageSource messageSource;
+
+ @Autowired
+ public EmailMakerServiceImpl(VelocityEngine velocityEngine, MessageSource messageSource) {
+ this.velocityEngine = velocityEngine;
+ this.messageSource = messageSource;
+ }
+
+ @Override
+ public String emailMaker(Map<String, String> text, String templateName, Locale locale) {
+ final String templateLocation = TEMPLATES_DEFAULT_PATH + templateName + TEMPLATES_DEFAULT_EXTENSION;
+ final Map<String, Object> model = new HashMap<>();
+ model.put("text", text);
+ model.put("messageSource", messageSource);
+ model.put("locale", locale);
+
+ return VelocityEngineUtils.mergeTemplateIntoString(
+ velocityEngine, templateLocation, EMAIL_CONTENT_ENCODING, model);
+ }
+
+ @Override
+ public String getSubject(String code, Locale locale, Object... args) {
+ return messageSource.getMessage(code, args, locale);
+ }
+
+}
--- /dev/null
+package de.spring.emails.services.impl;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import de.spring.emails.services.EmailService;
+
+
+@Service("emailService")
+public class EmailServiceImpl implements EmailService {
+ private static final String DEFAULT_FROM_VALUE = "noreply@gumartinm.name";
+
+ private final JavaMailSender mailSender;
+
+ @Autowired
+ public EmailServiceImpl(JavaMailSender mailSender) {
+ this.mailSender = mailSender;
+ }
+
+ @Override
+ public void sendEmail(String[] to, String subject, String text, boolean isHtml,
+ List<Resource> attachments, Map<String, Resource> inline) throws MessagingException {
+ Assert.notEmpty(to, "required email 'to' field");
+ Assert.hasLength(subject, "required email 'subject' field");
+ Assert.hasLength(text, "required email 'text' field");
+
+ final MimeMessage mimeMessage = mailSender.createMimeMessage();
+ final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true);
+
+ message.setTo(to);
+ message.setFrom(DEFAULT_FROM_VALUE);
+ message.setSubject(subject);
+ message.setSentDate(new Date());
+ message.setText(text, isHtml);
+
+ if (inline != null) {
+ for (Map.Entry<String, Resource> entry : inline.entrySet()) {
+ message.addInline(entry.getKey(), entry.getValue());
+ }
+ }
+
+ if (attachments != null) {
+ for (Resource attachment : attachments) {
+ message.addAttachment(attachment.getFilename(), attachment);
+ }
+ }
+
+
+ mailSender.send(mimeMessage);
+ }
+
+ @Override
+ @Async("asyncEmailSender")
+ public void sendEmailAsync(String[] to, String subject, String text, boolean isHtml,
+ List<Resource> attachments, Map<String, Resource> inline) throws MessagingException {
+ sendEmail(to, subject, text, isHtml, attachments, inline);
+ }
+}
--- /dev/null
+package de.spring.webservices.rest.controller;
+
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import de.spring.emails.services.EmailMakerService;
+import de.spring.emails.services.EmailService;
+
+@RestController
+@RequestMapping("/api/emails/")
+public class EmailController {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EmailController.class);
+ private static final String USER = "Gustavo Martin Morcuende";
+ private static final String USER_ADDRESS = "noemail@gumartinm.name";
+ private static final String TEMPLATE = "email-template";
+ private static final String SUBJECT_MESSAGE_KEY = "email.subject";
+
+ private final EmailService emailService;
+ private final EmailMakerService emailMakerService;
+
+ @Autowired
+ public EmailController(EmailService emailService, EmailMakerService emailMakerService) {
+ this.emailService = emailService;
+ this.emailMakerService = emailMakerService;
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ @ResponseStatus(HttpStatus.OK)
+ public void emails() throws MessagingException {
+ final String emailSubject = emailMakerService.getSubject(SUBJECT_MESSAGE_KEY, Locale.getDefault());
+ final String emailText = doEmailText();
+ final String[] to = { USER_ADDRESS };
+ final Map<String, Resource> inline = new HashMap<>();
+ inline.put("cid:mainlogo", new ClassPathResource("email/logo.png"));
+ try {
+ emailService.sendEmailAsync(to, emailSubject, emailText, true, null, inline);
+ } catch (MessagingException ex) {
+ LOGGER.error("Send email error", ex);
+ }
+ }
+
+ private String doEmailText() {
+ final String isoDateTime = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+ final Map<String, String> text = new HashMap<>();
+ text.put("user", USER);
+ text.put("date", isoDateTime);
+ return emailMakerService.emailMaker(text, TEMPLATE, Locale.getDefault());
+ }
+}
--- /dev/null
+email.subject = This is an example mail
+
+email.header1 = New email, just for you.
+email.header2 = User:
+email.message = New information about something important that you were expecting.
+
--- /dev/null
+<html>
+ <body>
+ <h1>#msg("email.header1")</h1>
+ <h2>#msg("email.header2")/h2>
+ <p>
+ ${text.user}
+ </p>
+ <p>
+ #msg("email.message")
+ </p>
+ <p>
+ ${text.date}
+ </p>
+ <img src='cid:mainlogo'>
+ </body>
+
+</html>
\ No newline at end of file
--- /dev/null
+#macro(msg $code)
+$messageSource.getMessage($code, null, $locale)
+#end
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ status: The level of internal Log4j events that should be logged to the console.
+ Valid values for this attribute are "trace", "debug", "info", "warn", "error" and "fatal".
+
+ monitorInterval: The minimum amount of time, in seconds, that must elapse before the file configuration is checked for changes.
+
+
+ see https://logging.apache.org/log4j/2.x/manual/configuration.html
+ -->
+<Configuration status="error" strict="true" monitorInterval="30"
+ name="XMLConfigTest" packages="org.apache.logging.log4j.test">
+
+ <!--
+ ALL > TRACE > DEBUG > INFO > WARN > ERROR > OFF
+
+ ERROR by default.
+ -->
+
+ <Appenders>
+ <Appender type="Console" name="STDOUT">
+ <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
+ </Appender>
+ </Appenders>
+ <Loggers>
+ <!--
+ General logging Spring.
+ -->
+ <Logger name="org.springframework" level="INFO" additivity="false">
+ <AppenderRef ref="STDOUT" />
+ </Logger>
+
+
+ <!--
+ Anything else will be using TRACE logging level.
+ -->
+ <Root level="INFO">
+ <AppenderRef ref="STDOUT"/>
+ </Root>
+ </Loggers>
+</Configuration>
--- /dev/null
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:mvc="http://www.springframework.org/schema/mvc"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
+
+ <!--
+ I am declaring my beans without the automatic annotation. :/
+ Better because we are saving memory but it requires more configuration.
+
+ See: org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
+ <mvc:annotation-driven/>
+ -->
+
+
+ <context:annotation-config />
+
+ <context:component-scan base-package="de.spring.webservices.rest"/>
+
+ <!--
+ Required beans for generating XML responses from Java objects using JAXB annotations
+ Jackson also works but it doesn't generate XML with namespaces... O.o
+
+ This implementation will be slower than the one using Jackson :( but I am going to use it just for WADL generation :)
+ -->
+ <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
+ <property name="packagesToScan" value="org.jvnet.ws.wadl"/>
+ </bean>
+ <bean id="jaxbConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
+ <constructor-arg ref="jaxbMarshaller" />
+ </bean>
+
+ <!-- Required beans for generating JSON responses from Java objects -->
+ <bean id="jsonObjectMapperFactory" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
+ p:indentOutput="true" p:failOnEmptyBeans="false">
+ <property name="featuresToDisable">
+ <array>
+ <util:constant static-field="com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES"/>
+ <util:constant static-field="com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSION"/>
+ </array>
+ </property>
+ </bean>
+
+ <util:list id="messageConverters">
+ <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" p:objectMapper-ref="jsonObjectMapperFactory"/>
+ <ref bean="jaxbConverter" />
+ <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
+ </util:list>
+
+
+ <bean name="handlerAdapter"
+ class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
+ <property name="webBindingInitializer">
+ <bean
+ class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
+ <!-- It enables us to use JSR-303 -->
+ <property name="validator">
+ <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
+ </property>
+ </bean>
+ </property>
+ <property name="messageConverters" ref="messageConverters" />
+
+
+ <property name="requestBodyAdvice">
+ <util:list>
+ <bean id="requestBodyAdvice" class="org.springframework.web.servlet.mvc.method.annotation.JsonViewRequestBodyAdvice"/>
+ </util:list>
+ </property>
+
+
+ <property name="responseBodyAdvice">
+ <util:list>
+ <bean id="responseBodyAdvice" class="org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice"/>
+ </util:list>
+ </property>
+ </bean>
+
+ <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
+
+ <mvc:default-servlet-handler />
+
+</beans>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:task="http://www.springframework.org/schema/task"
+
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/task
+ http://www.springframework.org/schema/task/spring-task-3.0.xsd">
+
+
+ <!-- Searches for beans in packages (instead of XML configuration we can use in this way annotations like @Service, @Endpoint, etc, etc) -->
+ <context:component-scan base-package="de.spring.emails"/>
+
+
+ <!-- Enable Asynchronous Spring Tasks -->
+ <task:annotation-driven />
+ <task:executor id="asyncEmailSender" pool-size="0-2" keep-alive="60" queue-capacity="2" rejection-policy="CALLER_RUNS" />
+
+
+ <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+ <property name="host" value="smtp.gumartinm.name"/>
+ <property name="port" value="25"/>
+ <property name="defaultEncoding" value="UTF-8"/>
+ </bean>
+
+ <bean id="mailVelocityEngine"
+ class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
+ <property name="velocityProperties">
+ <props>
+ <prop key="resource.loader">class</prop>
+ <prop key="class.resource.loader.class">
+ org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
+ </prop>
+ <prop key="velocimacro.library">email/macro.vm</prop>
+ </props>
+ </property>
+ </bean>
+
+ <bean name="mailMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
+ <property name="basename">
+ <value>email.email-messages</value>
+ </property>
+ <property name="defaultEncoding" value="UTF-8"/>
+ </bean>
+
+ <bean id="emailService"
+ class="de.spring.emails.services.impl.EmailServiceImpl">
+ <constructor-arg ref="mailSender" />
+ </bean>
+
+ <bean id="emailMakerService"
+ class="de.spring.emails.services.impl.EmailMakerServiceImpl">
+ <constructor-arg ref="mailVelocityEngine" />
+ <constructor-arg ref="mailMessageSource" />
+ </bean>
+
+</beans>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+ version="2.4">
+
+ <display-name>Spring Email Client: example</display-name>
+
+ <listener>
+ <listener-class>
+ org.springframework.web.context.ContextLoaderListener
+ </listener-class>
+ </listener>
+
+ <context-param>
+ <param-name>spring.profiles.active</param-name>
+ <param-value>${environment.profile}</param-value>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>
+ classpath*:spring-configuration/*.xml
+ </param-value>
+ </context-param>
+
+ <!-- Spring REST support -->
+ <servlet>
+ <servlet-name>spring-rest</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+ <init-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>classpath*:spring-configuration/mvc/rest/*.xml</param-value>
+ </init-param>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>spring-rest</servlet-name>
+ <!-- REQUIRED PATTERN BY swagger-ui. IT DOESN'T WORK WITH ANY OTHER o.O -->
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+</web-app>
--- /dev/null
+package de.spring.emails.services;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMultipart;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.GreenMailUtil;
+import com.icegreen.greenmail.util.ServerSetupTest;
+
+import de.spring.emails.services.impl.EmailServiceImpl;
+
+
+public class EmailServiceTest {
+ private static final String TO = "noemail@gumartinm.name";
+ private static final String SUBJECT = "Email test";
+ private static final String TEXT = "Some text in some email";
+
+ private GreenMail testSmtp;
+ private EmailService emailService;
+
+ @Before
+ public void setUp() throws Exception {
+ JavaMailSenderImpl senderImpl = new JavaMailSenderImpl();
+ senderImpl.setPort(3025);
+ senderImpl.setHost("localhost");
+ emailService = new EmailServiceImpl(senderImpl);
+
+ testSmtp = new GreenMail(ServerSetupTest.SMTP);
+ testSmtp.start();
+ }
+
+ @After
+ public void cleanup(){
+ testSmtp.stop();
+ }
+
+ @Test
+ public void whenSendEmailWithSuccesThenEmailArrivesToServer() throws MessagingException, IOException {
+ String[] to = { TO };
+
+ emailService.sendEmail(to, SUBJECT, TEXT, true, null, null);
+
+ Message[] messages = testSmtp.getReceivedMessages();
+ assertEquals(1, messages.length);
+ assertEquals(SUBJECT, messages[0].getSubject());
+ MimeMultipart mp = (MimeMultipart) messages[0].getContent();
+ String body = GreenMailUtil.getBody(mp.getBodyPart(0)).trim();
+ assertThat(body, CoreMatchers.containsString(TEXT));
+ }
+
+ @Test
+ public void whenSendEmailAsyncWithSuccesThenEmailArrivesToServer() throws MessagingException, IOException {
+ String[] to = { TO };
+
+ emailService.sendEmailAsync(to, SUBJECT, TEXT, true, null, null);
+
+ Message[] messages = testSmtp.getReceivedMessages();
+ assertEquals(1, messages.length);
+ assertEquals(SUBJECT, messages[0].getSubject());
+ MimeMultipart mp = (MimeMultipart) messages[0].getContent();
+ String body = GreenMailUtil.getBody(mp.getBodyPart(0)).trim();
+ assertThat(body, CoreMatchers.containsString(TEXT));
+ }
+
+}
--- /dev/null
+<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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>de.spring.emails</groupId>
+ <artifactId>spring-emails</artifactId>
+ <packaging>pom</packaging>
+ <version>1.0-SNAPSHOT</version>
+ <name>spring-emails</name>
+ <url>http://gumartinm.name</url>
+ <description>Emails with Spring Framework</description>
+ <organization>
+ <name>Gustavo Martin Morcuende</name>
+ <url>http://www.gumartinm.name</url>
+ </organization>
+ <scm>
+ <developerConnection>scm:git:http://git.gumartinm.name/JavaForFun</developerConnection>
+ <url>http://git.gumartinm.name/JavaForFun</url>
+ </scm>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <spring.version>4.2.5.RELEASE</spring.version>
+ </properties>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <properties>
+ <environment.profile>release</environment.profile>
+ </properties>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ </profile>
+ </profiles>
+
+ <dependencies>
+ <!--
+ 1/3 Required dependency for log4j 2 with slf4j: binding between log4j
+ 2 and slf4j
+ -->
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <version>2.3</version>
+ </dependency>
+ <!--
+ 2/3 Required dependency for log4j 2 with slf4j: log4j 2 maven plugin
+ (it is the log4j 2 implementation)
+ -->
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>2.3</version>
+ </dependency>
+ <!--
+ 3/3 Required dependency for getting rid of commons logging. This is
+ the BRIDGE (no binding) between Jakarta Commons Logging (used by Spring)
+ and whatever I am using for logging (in this case I am using log4j 2)
+ See: http://www.slf4j.org/legacy.html We need exclusions in every dependency using
+ Jakarta Commons Logging (see Spring dependencies below)
+ -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <version>1.7.12</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ <version>2.2.2</version>
+ </dependency>
+
+ <!-- Unitary and integration tests -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.0.43-beta</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ <!--
+ Required dependency for getting rid of commons logging and use my
+ own logging library (in my case I decided to use log4j 2 under slf4j)
+ -->
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ <version>${spring.version}</version>
+ <!--
+ Required dependency for getting rid of commons logging and use my
+ own logging library (in my case I decided to use log4j 2 under slf4j)
+ -->
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-oxm</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+
+
+ <!-- Required by spring-webmvc -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <version>4.0.0-b01</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!--
+ Jackson JSON Processor, required by spring-webmvc. See messageConverters
+ in rest-config.xml
+ -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.6.4</version>
+ </dependency>
+
+ <!--
+ Required by spring-context for using JSR-303. See LocalValidatorFactoryBean
+ in rest-config.xml
+ -->
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>1.1.0.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <version>5.2.2.Final</version>
+ </dependency>
+
+
+
+ <!-- Required dependency for VelocityEngineUtils -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ <version>1.7</version>
+ </dependency>
+ <dependency>
+ <groupId>velocity-tools</groupId>
+ <artifactId>velocity-tools</artifactId>
+ <version>2.0-beta1</version>
+ </dependency>
+
+ <!-- Required dependencies for SMTP client -->
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ <version>1.5.0-b01</version>
+ </dependency>
+
+
+ <!-- Unitary and integration tests -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>${spring.version}</version>
+ <scope>test</scope>
+ <!-- Required dependency for getting rid of commons logging and use my
+ own logging library (in my case I decided to use log4j 2 under slf4j) -->
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.icegreen</groupId>
+ <artifactId>greenmail</artifactId>
+ <version>1.5.0</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <build>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.19.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.19.1</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <Specification-Title>${project.description}</Specification-Title>
+ <Specification-Version>${project.version}</Specification-Version>
+ <Specification-Vendor>${project.organization.name}</Specification-Vendor>
+ <Implementation-Title>${project.description}</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/*IntegrationTest.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <includes>
+ <include>**/*IntegrationTest.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
+++ /dev/null
-package de.spring.emails.services;
-
-
-public interface EmailNotificationService {
-
- public void sendEmail();
-}
+++ /dev/null
-package de.spring.emails.services.impl;
-
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-
-import org.apache.velocity.app.VelocityEngine;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.mail.javamail.MimeMessagePreparator;
-import org.springframework.ui.velocity.VelocityEngineUtils;
-
-import de.spring.emails.services.EmailNotificationService;
-
-public class EmailNotificationServiceImpl implements EmailNotificationService {
- private final JavaMailSender mailSender;
- private final VelocityEngine velocityEngine;
-
- public EmailNotificationServiceImpl(JavaMailSender mailSender, VelocityEngine velocityEngine) {
- this.mailSender = mailSender;
- this.velocityEngine = velocityEngine;
- }
-
- @Override
- public void sendEmail() {
- final MimeMessagePreparator preparator = new MimeMessagePreparator() {
-
- public void prepare(MimeMessage mimeMessage) throws Exception {
- final MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
- message.setTo("toemail@example.com");
- message.setBcc("bccemail@example.com");
- message.setFrom(new InternetAddress("fromemail@example.com") );
- message.setSubject("New funny alert");
-
- final LocalDateTime dateTime = LocalDateTime.now();
- message.setSentDate(Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant()));
- Map model = new HashMap<>();
- model.put("newMessage", "");
-
- final String text = VelocityEngineUtils.mergeTemplateIntoString(
- velocityEngine, "templates/email-template.vm", "UTF-8", model);
- message.setText(text, true);
- }
- };
-
- mailSender.send(preparator);
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util"
-
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util.xsd">
-
- <!-- Searches for beans in packages (instead of XML configuration we can use in this way annotations like @Service, @Endpoint, etc, etc) -->
- <context:component-scan base-package="de.spring.emails"/>
-
- <bean id="smtpSession" class="org.springframework.jndi.JndiObjectFactoryBean">
- <property name="jndiName" value="java:comp/env/mail/Session" />
- </bean>
- <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
- <property name="session" ref="smtpSession" />
- </bean>
-
- <bean id="velocityEngine"
- class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
- <property name="velocityProperties">
- <value>
- resource.loader=class
- class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
- </value>
- </property>
- </bean>
-
- <bean id="emailNotificationService"
- class="de.spring.emails.services.impl.EmailNotificationServiceImpl">
- <constructor-arg ref="mailSender" />
- <constructor-arg ref="velocityEngine" />
- </bean>
-
-</beans>
+++ /dev/null
-<html>
- <body>
- <h3>Dears all</h3>
- <p>
- From - ${newMessage.name} / ${newMessage.email}
- </p>
- <h3>
- Something new and exciting.
- </h3>
- <p>
- ${newMessage.metadataLine}
- </p>
- <h3>
- For further information.
- </h3>
- <p>
- ${newMessage.message}
- </p>
- </body>
-
-</html>
\ No newline at end of file