Spring Email using Office 365

I’m using javamail package in raw format so far. I prefer to split all the communications (email, social media…) and run it as a spring REST API server. Here is the initial code snippet.

Take STS or Eclipse, Start a Spring Starter Project and follow the instructions given below.

Dependencies

Maven Dependencies are given below.

4.0.0

org.springframework.boot
spring-boot-starter-parent
2.1.8.RELEASE

org.grassfield
messenger
0.0.1-SNAPSHOT
messenger
MultiPlatformMessenger

1.8
UTF-8
UTF-8

org.springframework.boot
spring-boot-starter-mail

org.springframework.boot
spring-boot-starter-web

org.springframework.boot
spring-boot-starter-test
test

org.springframework.boot
spring-boot-maven-plugin

User.java

This is an entity class for the user.


package org.grassfield.msg;

import org.springframework.stereotype.Component;

/**
* The Class User.
* @author Murugapandian Ramaiah
* @version 1.0
*/
@Component
public class User {
private String email;

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

}

 

 

MailService.java

As the name implies this is the email sending service.


package org.grassfield.msg;

import java.io.UnsupportedEncodingException;
import java.net.InetAddress;

import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

@Service
public class MailService {
@Autowired
private JavaMailSender sender;

public void sendEmail (User user) {
SimpleMailMessage mail = new SimpleMailMessage();
mail.setFrom(user.getEmail());
mail.setTo(user.getEmail());
mail.setSubject("Testing Email Service");
mail.setText("Test email content");
this.sender.send(mail);
}

public void sendEmailWithAttachment(User user) throws MessagingException, UnsupportedEncodingException {
MimeMessage mimeMsg = this.sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, true);
helper.setFrom(new InternetAddress(user.getEmail(), user.getEmail()));
helper.setTo(user.getEmail());
helper.setSubject("Multipart email");
helper.setText("Content of this email");
this.sender.send(mimeMsg);
}

}

 

 

MailController.java

Here is the REST controller.


package org.grassfield.msg;

import java.io.UnsupportedEncodingException;

import javax.mail.MessagingException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* The Class MailController.
* @author Murugapandian Ramaiah
* @version 1.0
*/
@RestController
public class MailController {

/** The service. */
@Autowired
private MailService service;

/** The user. */
@Autowired
private User user;

@RequestMapping("test-plain-text-email")
public String testPlainTextEmail() {
user.setEmail("recipient@office365 email address");
service.sendEmail(user);
return "Email sent";
}

@RequestMapping("test-multipart-email")
public String testMultipartEmail() throws MessagingException, UnsupportedEncodingException {
user.setEmail("recipient@office365 email address");
service.sendEmailWithAttachment(user);
return "Email sent";
}

}

 

 

application.properties


spring.mail.host = smtp.office365.com
spring.mail.username = user@office 365 account
spring.mail.password = office 365 password
spring.mail.port=587
spring.mail.properties.mail.smtp.port=587
spring.mail.properties.mail.transport.protocol=smtps
spring.mail.properties.mail.smtp.auth = true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.ssl.enable=false
spring.mail.properties.mail.smtp.timeout=15000
spring.mail.properties.mail.smtp.connectiontimeout=15000
spring.mail.properties.mail.smtp.socketFactory.fallback = true
spring.mail.properties.mail.mail.debug=true
spring.mail.properties.mail.pop3.host=pop email address
spring.mail.properties.mail.pop3.port=110
spring.mail.properties.mail.pop3.starttls.enable=true
spring.mail.properties.mail.pop3.store=pop3s
spring.mail.properties.pop.pop3.username=pop username
spring.mail.properties.pop.pop3.password=pop password
spring.mail.properties.pop.pop3.folder=INBOX

 

 

Test

Test your services with these.

curl -X GET -i http://localhost:8080/test-plain-text-email

curl -X GET -i http://localhost:8080/test-multipart-email

Advertisements

Iterating a list in Thymeleaf

I started Apache tiles in 2010. It is convenient for the tasks I am doing. Thymeleaf picked up in parallel. By the time I read Thymeleaf template engine is trying to replace JSP, I left it as such and continue using Tiles 🙂

Anyway I wanted to see what is it. I was trying to iterate a list using th: namespace.

I have a controller like this, which returns a list of my entity.


@GetMapping (value = "/")
public String index(Locale locale, Model model) {
RestTemplate restTemplate = new RestTemplate();
List fromRest = restTemplate.getForObject("http://localhost:8081/api/v1/feeditems", List.class);
model.addAttribute("feeditems", (List)fromRest);
return "index";
}

And it is


<div th:if="${#lists.isEmpty(feeditems)}">
	Empty
</div>
<div th:if="not ${#lists.isEmpty(feeditems)}">
	Not Empty
</div>

<div th:each="post:${feeditems}">
	<div th:text="${post.title}"></div>
</div>

Here is the output:

Iterate through thymeleaf

I captured this the on the way to Kandy in Sri Lanka

I captured this the on the way to Kandy in Sri Lanka

I captured this Air India tail at Changi recently

SpringBoot with Spring Security

The aim of this post is to share the steps to create a SpringBoot web application with Security.

Tools

  • STS IDE
  • Open JDK 11

Dependencies

Create a new Spring Starter Project

SpringBoot with Spring Security Project Structure 2

Add the following dependencies to pom.xml

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.8.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 </dependencies>

View HTML files

error.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Error
</body>
</html>

hello.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text"> Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign out"/>
</form>
</body>
</html>

home.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

Click <a th:href="@{/hello}">here</a> to see a greeting.

</body>
</html>

login.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example</title>
</head>
<body>
<div th:if="${param.error}">Invalid username and password.</div>
<div th:if="${param.logout}">You have been logged out.</div>
<form th:action="@{/login}" method="post">
<div>
<label> User Name : <input type="text" name="username" />
</label>
</div>
<div>
<label> Password: <input type="password" name="password" />
</label>
</div>
<div>
<input type="submit" value="Sign In" />
</div>
</form>
</body>
</html>

Spring Configuration

SpringMvcConfig


package org.grassfield.parser;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/error").setViewName("error");
}
}

Security Configuration


package org.grassfield.parser;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();

return new InMemoryUserDetailsManager(user);
}
}

Spring Boot Application


package org.grassfield.parser;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AggregatorUiApplication {

public static void main(String[] args) {
SpringApplication.run(AggregatorUiApplication.class, args);
}

}

Project Structure

SpringBoot with Spring Security Project Structure

Run the project as a Spring Boot application and access http://localhost:8080 in the browser.

SpringBoot with Spring Security Project Structure 3

Ref: https://spring.io/guides/gs/securing-web/ It didnt work as expected as I need to amend a few things.

I captured this Air India tail at Changi recently

I captured this Air India tail at Changi recently

 

Could not write JSON: (was java.lang.NullPointerException)

It was a dirty exception I faced few weeks back. I was trying to to consume a REST API call using postForEntity.

Unfortunately my ID variable was in primitive long instead of Long. I hit with the following error. I had been roaming around without a clue.

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException)

Ref: https://stackoverflow.com/questions/24805043/org-springframework-http-converter-httpmessagenotwritableexception-could-not-wr

I shot this soft white sand beach at Coney Island recently

I shot this soft white sand beach at Coney Island recently

Failed to read auto-increment value from storage engine

One after another – This was a strange exception I received today. I am not sure if this is a bug of JPA or underlying MySQL.


SQL Error: 1467, SQLState: HY000

Failed to read auto-increment value from storage engine

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement] with root cause

java.sql.SQLException: Failed to read auto-increment value from storage engine

I was pretty much surprised, what else!

Server version: 10.1.36-MariaDB

Spring Boot :: (v2.1.7.RELEASE)

I tried this.


ALTER TABLE `feed_item_info` AUTO_INCREMENT =1

It does not help me.

Later –

ALTER TABLE `feed_item_info` ORDER BY `index`

It throws the following warning.

Warning: #1105 ORDER BY ignored as there is a user-defined clustered index in the table ‘feed_item_info’

But it was working.

Ref:https://stackoverflow.com/questions/17690926/failed-to-read-auto-increment-value-from-storage-engine-error-number-1467

I shot this recently, at the seashore which borders Singapore and Malaysia

I shot this recently, at the seashore which borders Singapore and Malaysia

Tomcat started in Eclipse. But application is not started

This is a strange problem after I migrated from old version of STS to STS4.


Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version: Apache Tomcat/9.0.13
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built: Nov 2 2018 14:27:55 UTC
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server number: 9.0.13.0
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Name: Windows 10
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Version: 10.0
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Architecture: amd64
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Java Home: C:\Program Files\Java\jdk1.8.0_181\jre
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Version: 1.8.0_181-b13
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Vendor: Oracle Corporation
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_BASE: D:\Pandian\Documents\workspace_crm\.metadata\.plugins\org.eclipse.wst.server.core\tmp0
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_HOME: H:\Dev\apache-tomcat-9.0.13
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.base=D:\Pandian\Documents\workspace_crm\.metadata\.plugins\org.eclipse.wst.server.core\tmp0
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.home=H:\Dev\apache-tomcat-9.0.13
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dwtp.deploy=D:\Pandian\Documents\workspace_crm\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.endorsed.dirs=H:\Dev\apache-tomcat-9.0.13\endorsed
Sep 19, 2019 5:20:19 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dfile.encoding=Cp1252
Sep 19, 2019 5:20:19 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: Loaded APR based Apache Tomcat Native library [1.2.18] using APR version [1.6.5].
Sep 19, 2019 5:20:19 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
Sep 19, 2019 5:20:19 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
Sep 19, 2019 5:20:19 AM org.apache.catalina.core.AprLifecycleListener initializeSSL
INFO: OpenSSL successfully initialized [OpenSSL 1.1.1 11 Sep 2018]
Sep 19, 2019 5:20:19 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Sep 19, 2019 5:20:21 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Sep 19, 2019 5:20:21 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-nio-8009"]
Sep 19, 2019 5:20:21 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Sep 19, 2019 5:20:21 AM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 2281 ms
Sep 19, 2019 5:20:21 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Catalina]
Sep 19, 2019 5:20:21 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/9.0.13
Sep 19, 2019 5:20:22 AM org.apache.jasper.servlet.TldScanner scanJars
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Sep 19, 2019 5:20:22 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
Sep 19, 2019 5:20:22 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-nio-8009"]
Sep 19, 2019 5:20:22 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1107 ms

  • Removed the server tomcat and added again
  • Removed few .props files from workspace\.metadata\.plugins\org.eclipse.core.runtime\.settings.
  • changed the server locations in Server overview screen

Nothing helped except Maven Build.. Still Open. I’ll update if I have a solution later.

I captured this Green corridor at Royal Botanical Gardens, Peradeniya

I captured this Green corridor at Royal Botanical Gardens, Peradeniya

detached entity passed to persist

I am in mid of a crud operation. This exception hit me today, when I updated an entity with @ManyToOne(cascade = CascadeType.ALL) relationship.

org.hibernate.PersistentObjectException: detached entity passed to persist: org.grassfield.feed.entity.Feed

To solve this bidirectional consistency problem, I need to change CascadeType.ALL to CasecaseType.MERGE.

Recently I shot this pretty lady (or mighty gentleman?) at Coney Island. This has no relation with this post anyway.

Recently I shot this pretty lady (or mighty gentleman?) at Coney Island. This has no relation with this post anyway.