Docker deployment for Spring boot application

Docker deployment of spring boot application is a hot topic in the recent years, here are the detailed steps:Create a spring boot application: start with https://start.spring.io/, select Reactive web and Reactive Mongodb and provide group and artifact, generate project.

Import the generated project into STS (or eclipse)

Spring boot application

package com.leespace.bookstore;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;

import com.mongodb.ConnectionString;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;

@SpringBootApplication(exclude = { MongoAutoConfiguration.class, MongoDataAutoConfiguration.class })
@EnableReactiveMongoRepositories
@AutoConfigureAfter(EmbeddedMongoAutoConfiguration.class)
public class BookstoreMongoSpringBootApplication extends AbstractReactiveMongoConfiguration {

	
    private final Environment environment;
	 
    public BookstoreMongoSpringBootApplication(Environment environment) {
        this.environment = environment;
    }
	public static void main(String[] args) {
		SpringApplication.run(BookstoreMongoSpringBootApplication.class, args);
	}

	@Override
	public MongoClient reactiveMongoClient() {
		MongoClient mongoClient = MongoClients.create(new ConnectionString(environment.getProperty("spring.data.mongodb.uri")));
		return mongoClient;
	}

	@Override
	protected String getDatabaseName() {
		return "leespacedb";
	}
}

Create Entity class

package com.leespace.bookstore.entity;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.Data;
import lombok.RequiredArgsConstructor;

@Data
@Document
@RequiredArgsConstructor
public class Product {
	@Id
	private int id;
	private String name;
	private String description;
	private String author;
	private String category;
	private double price;
	private String imageUrl;
	
	public Product(int id, String name, String description, String author, String category, double price,
			String imageUrl) {
		super();
		this.id = id;
		this.name = name;
		this.description = description;
		this.author = author;
		this.category = category;
		this.price = price;
		this.imageUrl = imageUrl;
	}
	
	
}

Create ReactiveRepository

package com.leespace.bookstore.repository;

import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Component;

import com.leespace.bookstore.entity.Product;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Component
public interface ReactiveProductRepository extends ReactiveCrudRepository<Product, String> {
    Flux<Product> findByName(String name);

    Flux<Product> findByName(Mono<String> name);

    Mono<Product> findByNameAndImageUrl(Mono<String> name, String imageUrl);

    @Query("{ 'name': ?0, 'imageUrl': ?1}")
    Mono<Product> findByNameAndImageUrl(String name, String imageUrl);
}

Create RestController

package com.leespace.bookstore.rest;

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

import com.leespace.bookstore.entity.Product;
import com.leespace.bookstore.repository.ReactiveProductRepository;

import reactor.core.publisher.Flux;

@RestController
public class ProductController {

	@Autowired
	private ReactiveProductRepository propductRepository;

	@GetMapping("/products")
	public Flux<Product> getAllProducts() {
		return propductRepository.findAll();
	}

}

Create Dockerfile

FROM frolvlad/alpine-oraclejdk8:slim
EXPOSE 8080
RUN mkdir -p /app/
ADD target/bookstore-mongo-spring-boot-0.0.1-SNAPSHOT.jar /app/bookstore-mongo-spring-boot.jar
ENTRYPOINT ["java", "-jar", "/app/bookstore-mongo-spring-boot.jar"]

Build docker image

docker build . -t bookstore-mongo-spring-boot

Run spring boot app in a docker container.

Firstly clean up stopped containers: 
docker rm $(docker ps -a -q)
docker run --name="bookstore-mongo-spring-boot-A" --restart unless-stopped --publish 9001:8080 --detach bookstore-mongo-spring-boot 

docker run --name="bookstore-mongo-spring-boot-B" --restart unless-stopped --publish 9002:8080 --detach bookstore-mongo-spring-boot 

You can insert some records in the product collection with the following:

db.product.insert({ “_id” : 1,”name”:”Java”, “description” : “Java introduction”, “author” : “Jack”, “category” : “Book”, “price” : 234, “imageUrl” : “javaUrl”, “_class” : “com.leespace.bookstore.entity.Product”} );

db.product.insert({ “_id” : 2, “name” : “Nodejs”, “description” : “Nodejs introduction”, “author” : “John”, “category” : “Book”, “price” : 134, “imageUrl” : “Nodejsurl” ,”_class” : “com.leespace.bookstore.entity.Product”})

You can check http://localhost:9001/products or http://localhost:9002/products
The expected rest response will be like :

{
"id": 1,
"name": "Java",
"description": "Java introduction",
"author": "Jack",
"category": "Book",
"price": 234,
"imageUrl": "javaUrl"
},
{
"id": 2,
"name": "Nodejs",
"description": "Nodejs introduction",
"author": "steve",
"category": "Book",
"price": 134,
"imageUrl": "Nodejsurl"
}

Read More

java.util.concurrent CountDownLatch explained.


import java.util.List;
import java.util.concurrent.CountDownLatch;

public class ThreadRunner implements Runnable {
    private List<  String> eventList;
    private CountDownLatch countDownLatch;
 
    public ThreadRunner(List<  String> outputScraper, CountDownLatch countDownLatch) {
        this.eventList = outputScraper;
        this.countDownLatch = countDownLatch;
    }
 
    @Override
    public void run() {
        doSomeWork();
        eventList.add("event " + countDownLatch.getCount());
        countDownLatch.countDown();
    }

	private void doSomeWork() {
		System.out.println("do something..."   + countDownLatch.getCount());
	}
}

package com.leespace.training.thread;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

public class ThreadRunnerTest {
	@Test
	public void whenParallelProcessing_thenMainThreadWillBlockUntilCompletion() throws InterruptedException {
        int counter = 6;
		List<String> events = Collections.synchronizedList(new ArrayList<>());
		CountDownLatch countDownLatch = new CountDownLatch(counter);
		List<Thread> workers = Stream.generate(() -> new Thread(new ThreadRunner(events, countDownLatch))).limit(counter)
				.collect(Collectors.toList());

		workers.forEach(Thread::start);
		countDownLatch.await();
		events.add("This ia the last event");

		events.forEach(a -> System.out.println(a));

	}
}

Output:

do something…6
do something…6
do something…4
do something…3
do something…2
do something…1
event 6
event 5
event 4
event 3
event 2
event 1
This ia the last event

Read More

Javascript: Processing collections with map, reduce, and filter

Map executes that callback on every element within it, returning a new array with all of the values that the callback returned. Map passes three arguments to your callback:

  • The current item in the array
  • The current index
  • The entire array you called map on

Filter takes an array, and filters out unwanted elements.Filter passes your callback three arguments:

  • The current item
  • The current index
  • The array you called filter on

Reduce takes all of the elements in an array, and reduces them into a single value. Reduce passes your callback four arguments:

  • The current value
  • The previous value
  • The current index
  • The array you called reduce on
  •  
     	 
    var isEven = num => num % 2 === 0;
    var square = num => num * num;
    var add = (a, b) => a + b;
      
    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
      
    arr.filter(isEven).map(square).reduce(add); 
    
    
    Output: 220
    

Read More

How to sort javascript array with objects?


var people = [
    {Name: "Jack Lee", Age: 21},
    {Name: "Jack Ma", Age:31},
    {Name: "Steve Liu", Age: 12},
    {Name: "Alice  Xu", Age: 42}
];


//Sort by name
people.sort(function(a,b){
   return   (a.Name.toLowerCase() > b.Name.toLowerCase()) ? 1 : -1;
});

//Sort by ES6 arrow function
people.sort((a,b)=>{
   return   (a.Name.toLowerCase() > b.Name.toLowerCase()) ? 1 : -1;
});

Sorted array by name:
>people
 
0: {Name: "Alice  Xu", Age: 42}
1: {Name: "Jack Lee", Age: 21}
2: {Name: "Jack Ma", Age: 31}
3: {Name: "Steve Liu", Age: 12} 


//sort by age
 people.sort(function(a,b){
   return   (a.Age > b.Age)? 1 : -1;
});


//Sort by ES6 arrow function
 people.sort((a,b)=>{
   return   (a.Age > b.Age)? 1 : -1;
});

Sorted array by age:
>people

0: {Name: "Steve Liu", Age: 12}
1: {Name: "Jack Lee", Age: 21}
2: {Name: "Jack Ma", Age: 31}
3: {Name: "Alice  Xu", Age: 42} 

Read More

Use java lombok in POJO.

You need to add lombok dependency into your pom file:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.18</version>
    <scope>provided</scope>
</dependency>

and you need to install lombok ito your IDE with this command:
java -jar lombok-1.16.18.jar

The @Data annotation will give you everything including getter/Setter/ hashcode /equals and toString methods.


import lombok.Data;

@Data
public class Person {
	private Integer age;
	private String name;
}

Read More

Java Stream Reduce examples:

import java.util.Arrays;
import java.util.List;

public class JavaStreamReducer {

	public static void main(String[] args) {

		// Reduce Array to String.
		String[] people = { "Jack", "Steve", "Alice" };
		Arrays.stream(people).reduce((x, y) -> x + " | " + y).ifPresent(s -> System.out.println(s));
		// Reduce List to String.

		System.out.println("\n");
		List<String> peoplelist = Arrays.asList(people);
		peoplelist.stream().reduce((x, y) -> x + "," + y).ifPresent(s -> System.out.println(s));
		System.out.println("\n");

		// function programming with Stream
		String greeting = greet(peoplelist);
		System.out.println(greeting + "\n");

		Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		int sum = Arrays.stream(numbers).reduce(0, (x, y) -> x + y);
                //	 or with .reduce(0, Integer::sum);
		System.out.println("Sum of Array: " + sum);
		System.out.println("\n");
		// Reduce List to sum.
		List<Integer> list = Arrays.asList(numbers);
		sum = list.stream().reduce(0, (x, y) -> x + y);
		System.out.println("Sum of List: " + sum);

		List<Integer> list2 = Arrays.asList(2, 3, 4);
		// Here result will be 2*2 + 2*3 + 2*4 that is 18.
		int res = list2.parallelStream().reduce(2, (s1, s2) -> s1 * s2, (p, q) -> p + q);
		System.out.println(res);
	}

	public static String greet(List<String> names) {
		String greeting = names.stream().map(name -> name + " ").reduce("Welcome ", (acc, name) -> acc + name);
		return greeting + "!";
	}

output:
Jack | Steve | Alice


Jack,Steve,Alice


Welcome Jack Steve Alice !

Sum of Array: 55


Sum of List: 55
18

 

Read More

How to filter a map with Java Stream?


class Person {
	private Integer age;
	private String name;
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Person(Integer age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + "]";
	}

}


import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class JavaStreamMapFilter {

	public static void main(String[] args) {

		
		//Example of stream filter return  String
		Map<String, String> nameMap  = new HashMap<>();
		nameMap.put("JL", "Jack Lee");
		nameMap.put("JM", "Jack Ma");
		nameMap.put("SL", "Steve Liu");
		nameMap.put("AX", "Alice Xu");
 
        String result = nameMap.entrySet().stream()
                .filter(map -> "Jack Ma".equals(map.getValue()))
                .map(map -> map.getValue())
                .collect(Collectors.joining());

        System.out.println("\n\nResult : " + result);
		
		//Example of stream filter return  a Map
        Map<String, Person>   peopleMap = getMap();
		System.out.println("Initial  map :  ");
		peopleMap.forEach((k, v) -> System.out.println(k + ":" + v));
		Map<String, Person> oldPeopleMap = peopleMap.entrySet().stream().filter(p -> p.getValue().getAge() > 30)
				.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

		System.out.println("\n\nAfter filtering, here is the map :  ");
		oldPeopleMap.forEach((k, v) -> System.out.println(k + ":" + v));
	}

	private static Map<String, Person> getMap() {
		Map<String, Person> peopleMap = new HashMap<>();
		Person person1 = new Person(21, "Jack Lee");
		peopleMap.put("JL", person1);
		Person person2 = new Person(31, "Jack Ma");
		peopleMap.put("JM", person2);
		Person person3 = new Person(12, "Steve Liu");
		peopleMap.put("SL", person3);
		Person person4 = new Person(42, "Alice XU");
		peopleMap.put("AX", person4);

		return peopleMap;
	}

}



Here the output:

Result : Jack Ma
Initial  map :  
JL:Person [age=21, name=Jack Lee]
JM:Person [age=31, name=Jack Ma]
AX:Person [age=42, name=Alice XU]
SL:Person [age=12, name=Steve Liu]


After filtering, here is the map :  
JM:Person [age=31, name=Jack Ma]
AX:Person [age=42, name=Alice XU]

Read More

How to filter a List with Java Stream?

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


class Person {
	private Integer age;
	private String name;
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Person(Integer age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + "]";
	}

}

public class JavaStreamFilter {

	public static void main(String[] args) {

		List<Person> people = getArrayList();
		List<Person> oldPeople = people.stream().filter(p -> p.getAge() > 30).collect(Collectors.toList());
		System.out.println("\nThe sorted list:");
		displayList(oldPeople);
	}

	private static void displayList(List<Person> people) {

		for (Person p : people) {
			System.out.println(p);
		}
	}

	private static List<Person> getArrayList() {
		List<Person> people = new ArrayList<Person>();
		Person person1 = new Person(21, "Jack Lee");
		people.add(person1);
		Person person2 = new Person(31, "Jack Ma");
		people.add(person2);
		Person person3 = new Person(12, "Steve Liu");
		people.add(person3);
		Person person4 = new Person(42, "Alice XU");
		people.add(person4);

		return people;
	}




The result is: 
The sorted list:
Person [age=31, name=Jack Ma]
Person [age=42, name=Alice XU]

Read More

Add copyright statement in all source files.

You have completed you development for a large project and QA finished the tests as well, everything works perfectly,so you may decide to celebrate a little bit. Before you come out a great plan, your manager come over and told you there is some tedious thing to do: You need to add a copyright statement to each and every source code file. Jus like many people, you may start quickly estimating how many files you need to CTRL+C and CTRL+V, and then do a integration test before you can commit your change.If you are a hard-working employee, you may your change right way, but if you are a ‘lazy’ programmer, you may try something differently:

As you are a programmer, you are good at coding, why not write a program to do the job, here is the jave code:

Step 1: Find out all the files need to be updated.


import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class ListFilesUtil {
	public List <String> listFilesUnderDirectories(String directoryName) {
		List<String> fileList = new ArrayList<>();
		File directory = new File(directoryName);
		File[] fList = directory.listFiles();
		for (File file : fList) {
			if (file.isFile() && file.getAbsolutePath().endsWith(".js")) {
				fileList.add(file.getAbsolutePath()); 
			} else if (file.isDirectory()) {
				fileList.addAll(listFilesUnderDirectories(file.getAbsolutePath()));
			}
		}
		return fileList;
	}
}

Step2: For each file, re-write it with the copy right content


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

public class RewriteFile {

	public static void main(String[] args) {
		
		String targetDirectory = "target_directory"; 
		RewriteFile rf = new RewriteFile();
		String licence =  rf.readFile("copyright.txt") ;
		ListFilesUtil util = new ListFilesUtil(); 
		List <String> fileList = util.listFilesUnderDirectories(targetDirectory);
		
		fileList.forEach(file->{
			 rf.rewriteFile(file, licence);
		});
	}

	public void rewriteFile(String fileName, String licence) {

		try {
			
			StringBuffer sb = new StringBuffer();
			try {
				FileReader fr = new FileReader(fileName);
				BufferedReader br = new BufferedReader(fr);
				String line = "";
				
				line = br.readLine();
				while (line != null) {
					sb.append(line+"\n");
					line = br.readLine();
				}
				br.close();
			} catch (FileNotFoundException e) {
				System.out.println("File was not found!");
			} catch (IOException e) {
				System.out.println("No file found!");
			}
			String newContext = licence + "\n"+  sb.toString();
			
			FileWriter fw = new FileWriter(fileName, false);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(newContext);
		 
			bw.close();
		} catch (FileNotFoundException e) {
			System.out.println("Error1!");
		} catch (IOException e) {
			System.out.println("Error2!");
		}
	}

	public String readFile(String fileName) {

		StringBuffer sb = new StringBuffer();
		try {
			FileReader fr = new FileReader(fileName);
			BufferedReader br = new BufferedReader(fr);
			String line = "";

			while ((line = br.readLine()) != null) {
				sb.append(line +"\n");
			}
			br.close();
		} catch (FileNotFoundException e) {
			System.out.println("File was not found!");
		} catch (IOException e) {
			System.out.println("No file found!");
		}
		return sb.toString();
	}
}

Read More