How to expose multiple ports of Container to Docker?

I used EXPOSE 8080 to expose 8080 port to the docker host in my earliest post.

I need to expose two ports, one for application access and another one for JMX monitoring.

EXPOSE 8080 48080

While running the docker, use one -p parameter for each port to be exposed as given below,

$ docker run -p 6666:8080 -p 58080:48080 messenger

 

Creating docker image for SpringBoot REST gateway

This post documents the steps I performed to containerize my REST application built with Spring Boot.

This uses the following tools

  1. Spring Tool Suite 4
  2. Maven – STS inbuilt
  3. Open JDK (Built by Azul)
  4. Docker

 

Step 1: Build your spring boot application using Maven:

SpringBoot and Docker 01

This will build the file and jar file will be copied to the target folder of your project folder.

[INFO] --- maven-jar-plugin:3.1.2:jar (default-jar) @ messenger ---
[INFO] Building jar: D:\Pandian\Documents\workspace_hamsa\messenger\target\messenger-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.8.RELEASE:repackage (repackage) @ messenger ---
[INFO] Replacing main artifact with repackaged archive
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ messenger ---
[INFO] Installing D:\Pandian\Documents\workspace_hamsa\messenger\target\messenger-0.0.1-SNAPSHOT.jar to C:\Users\barat\.m2\repository\org\grassfield\messenger\0.0.1-SNAPSHOT\messenger-0.0.1-SNAPSHOT.jar
[INFO] Installing D:\Pandian\Documents\workspace_hamsa\messenger\pom.xml to C:\Users\barat\.m2\repository\org\grassfield\messenger\0.0.1-SNAPSHOT\messenger-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.659 s
[INFO] Finished at: 2019-10-12T08:57:43+08:00
[INFO] ------------------------------------------------------------------------

You may see above. The jar file messenger-0.0.1-SNAPSHOT.jar is created in the target folder.

Step 2: Create Dockerfile

We start building our own container now. We need to define the docker configuration in a Dockerfile. It will be placed in the root of project folder.

Following is the Docker file for this project.

#this uses alpine base image with openjdk 8
FROM openjdk:8-jdk-alpine

#Maintainer contact details
MAINTAINER Murugapandian Ramaiah 

#where to persist the data generated by the container.
VOLUME /tmp

#which port of this container is to be exposed outside
EXPOSE 8080

#specify the SpringBoot bulky jar
ARG JAR_FILE=target/messenger-0.0.1-SNAPSHOT.jar
ADD ${JAR_FILE} messenger.jar

#specify how to execute the application
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-jar", "/messenger.jar"]

Step 3: Build the container image

SSH the project folder to the /tmp partition of docker host Execute the following command.

$ cd /tmp/messenger
$ docker build -t messenger .
Step 1/7 : FROM openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/7 : MAINTAINER Murugapandian Ramaiah
 ---> Using cache
 ---> 4bb0470845c7
Step 3/7 : VOLUME /tmp
 ---> Using cache
 ---> 232d70e90d54
Step 4/7 : EXPOSE 8080
 ---> Using cache
 ---> 0a1d2e61d4a4
Step 5/7 : ARG JAR_FILE=target/messenger-0.0.1-SNAPSHOT.jar
 ---> Using cache
 ---> f179766aa87f
Step 6/7 : ADD ${JAR_FILE} messenger.jar
 ---> bc76f586ac0b
Step 7/7 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-jar", "/messenger.jar"]
 ---> Running in 0646cdbee037
Removing intermediate container 0646cdbee037
 ---> 574eb9b9004b
Successfully built 574eb9b9004b
Successfully tagged messenger:latest

The container image is created

$ docker image ls
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
messenger            latest              574eb9b9004b        23 minutes ago      123MB
openjdk              8-jdk-alpine        a3562aa0b991        5 months ago        105MB
pandian/centos_ssh   latest              7f019be4424c        14 months ago       294MB
centos               latest              5182e96772bf        14 months ago       200MB
ubuntu               latest              735f80812f90        14 months ago       83.5MB
hello-world          latest              2cb0d9787c4d        15 months ago       1.85kB

messenger is added.

Step 4: run the container.

$ docker run -p 6666:8080 messenger

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.8.RELEASE)

2019-10-12 07:11:42.372  INFO 1 --- [           main] org.grassfield.msg.MessengerApplication  : Starting MessengerApplication v0.0.1-SNAPSHOT on 0f9f70c91b73 with PID 1 (/messenger.jar started by root in /)
2019-10-12 07:11:42.375  INFO 1 --- [           main] org.grassfield.msg.MessengerApplication  : No active profile set, falling back to default profiles: default
2019-10-12 07:11:43.394  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-10-12 07:11:43.430  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-10-12 07:11:43.431  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.24]
2019-10-12 07:11:43.530  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-10-12 07:11:43.530  INFO 1 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1118 ms
2019-10-12 07:11:43.758  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-10-12 07:11:44.005  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-10-12 07:11:44.008  INFO 1 --- [           main] org.grassfield.msg.MessengerApplication  : Started MessengerApplication in 2.072 seconds (JVM running for 2.438)

I endorse this post by Rajeev – https://www.callicoder.com/spring-boot-docker-example/

Dont miss this carving in Temple of tooth, Kandy (Summer 2019)

Dont miss this carving in Temple of tooth, Kandy (Summer 2019)

 

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

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

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

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.

 

 

ClassCastException: RestTemplate returning List instead of List

Problem:

I have a POJO entity, say org.grassfield.Employee. I expose this as a REST services.

I consume this REST service,


List employeeList = restTemplate.getForObject(
"http://localhost:8080/api/v1/employees" ,
List.class);

Instead of getting Employee object, I’m getting LinkedHashMap. So my code fails with ClassCastException: RestTemplate returning List<LinkedHashMap> instead of List<Employee>

Solution:

Jackson uses LinkedHashMap to Serialize/De serialize the entity objects. When I consume the service using parameterized type reference, it works fine.


ResponseEntity<List> response = restTemplate.exchange(
"http://localhost:8080/api/v1/employees" ,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List>(){});
List EmployeeList = response.getBody();