Tuesday, August 9, 2022

Spring Boot2 + Reactive Spring Data + Reactive Elasticsearch + CRUD

Spring Boot + Reactive Spring Data + Reactive Elasticsearch example

Elasticsearch: 

By official documentation Elasticsearch is a distributed, free and open search and analytics engine for all types of data, including textual, numerical, geospatial, structured, and unstructured. Elasticsearch is built on Apache Lucene and was first released in 2010 by Elasticsearch N.V. (now known as Elastic).

Technology

  • Spring Boot 2.7.2
  • Java 17 (Zulu)
  • Docker
  • Maven 
  • Elasticsearch 7.17.5
  • IntelliJ IDEA 
  • Postman

Project Structure


























Configuration Spring Boot project 


pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>SpringDataElasticsearch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringDataElasticsearch</name>
<description>Integration SpringData + Elasticsearch</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>


application.yml: Configuration reactive elasticsearch 


server:
port: 9000
servlet:
context-path: /
spring:
data:
elasticsearch:
client:
reactive:
endpoints: localhost:9200
elasticsearch:
rest:
uris: http://localhost:9200

Model


package com.henry.SpringDataElasticsearch.model;

import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.annotation.Id;

@Document(indexName = "users")
public class User {

@Id
private Long id;
private String name;
private String lastName;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}

Repository


package com.henry.SpringDataElasticsearch.repository;

import com.henry.SpringDataElasticsearch.model.User;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;

@Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {

Flux<User> findByName(String name);
}

Service


package com.henry.SpringDataElasticsearch.service;

import com.henry.SpringDataElasticsearch.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface UserService {
Mono<User> save(User user);

Mono<User> update(Long id, User user);

Mono<Void> delete(Long id);

Mono<User> findOne(Long id);

Flux<User> findAll();

Flux<User> findByName(String name);
}

ServiceImpl


package com.henry.SpringDataElasticsearch.service.impl;

import com.henry.SpringDataElasticsearch.model.User;
import com.henry.SpringDataElasticsearch.repository.UserRepository;
import com.henry.SpringDataElasticsearch.service.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class UserServiceImpl implements UserService {

private final UserRepository userRepository;

public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public Mono<User> save(User user) {
return userRepository.save(user);
}

@Override
public Mono<User> update(Long id, User user) {
return userRepository.findById(id)
.flatMap(u -> {
u.setName(user.getName());
u.setLastName(user.getLastName());
return userRepository.save(u);
});
}

@Override
public Mono<Void> delete(Long id) {
var del = userRepository.deleteById(id);
return del;
}

@Override
public Mono<User> findOne(Long id) {
return userRepository.findById(id);
}

@Override
public Flux<User> findAll() {
return userRepository.findAll();
}

@Override
public Flux<User> findByName(String name) {
return userRepository.findByName(name);
}
}

Controller


package com.henry.SpringDataElasticsearch.controller;

import com.henry.SpringDataElasticsearch.model.User;
import com.henry.SpringDataElasticsearch.service.UserService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/save")
public Mono<User> add(@RequestBody User user) {
return userService.save(user);
}

@PutMapping("/update/{id}")
public Mono<User> update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}

@GetMapping("/findOne/{id}")
public Mono<User> findOne(@PathVariable Long id) {
return userService.findOne(id);
}

@GetMapping("/all")
public Flux<User> findAll() {
return userService.findAll();
}

@GetMapping("/findByName/{name}")
public Flux<User> findByName(@PathVariable String name) {
return userService.findByName(name);
}

@DeleteMapping("/delete/{id}")
public Mono<Void> delete(@PathVariable Long id) {
return userService.delete(id);
}

}

Downloading and installing Elasticsearch


docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.17.5


Run & Test


Run Spring Boot application with command: mvn spring-boot:run. by console, IntelliJ etc.


POST
localhost:9000/users/save















PUT
localhost:9000/users/update/1
















GET
localhost:9000/users/all


















localhost:9000/users/findByName/henry
















DELETE
localhost:9000/users/delete/1












Basic examples  retrieves documents in Elasticsearch are represented in JSON format.

localhost:9200/_search?q=Henry





















http://localhost:9200/_search?q=lastName:user




















Source Code


Here on GitHub.






References.

https://www.elastic.co/what-is/elasticsearch
https://www.elastic.co/blog/a-practical-introduction-to-elasticsearch
https://www.springcloud.io/post/2022-03/getting-started-with-spring-webflux/#gsc.tab=0
https://piotrminkowski.com/2019/10/25/reactive-elasticsearch-with-spring-boot/
https://mkyong.com/spring-boot/spring-boot-spring-data-elasticsearch-example/
https://www.youtube.com/watch?v=bYiNlCaaRiI
https://www.youtube.com/watch?v=rfjsaccL_e0
https://www.youtube.com/watch?v=uSFNaYlc5ek

Friday, July 29, 2022

Spring Boot 2.4.4, Kotlin 1.6, Basic Auth, JWT and LDAP


In this post. I will share a basic example,  how to integrate Spring Boot , Kotlin , Basic Auth, JWT and LDAP. However since Spring Security 5 the oauth2 is deprecated so I suggest to use Open Source Identity, Keycloak, OpenIAM, CAS  etc. Or We will wait for new version Spring 6 in Nov 2022  this version will come available Spring Authorization Server 1.0

Basic Auth 

Basic Authentication is a Method HTTP where we provide the username and password to the people making requests.

JSON Web Token

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

LDAP 

I this example used test-server.ldif LDIF  file provide by Spring.


Creating a Spring Boot Application


In this examples we're integration Spring Boot, Kotlin, Basic Auth and Ldap .ldif file.


Spring Boot Rest  API 

This is API

Methods URLs
POST /oauth/token
GET /principal
GET /users

Technology

  • Spring Boot 2.4.4
  • Kotlin 1.6.21
  • JWT
  • OAuth2
  • Java 17 (Zulu)
  • Maven 
  • IntelliJ IDEA 
  • test-server.ldif

Project Structure





















Configuration Spring Boot project 


pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>SpringBoot2KotlinBasicAuthJWTLdap</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot2KotlinBasicAuthJWTLdap</name>
<description>Spring Boot 2 + Kotlin + Basic Auth + JWT + LDAP</description>
<properties>
<java.version>17</java.version>
<kotlin.version>1.6.21</kotlin.version>
</properties>
<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.ldap/spring-ldap-core -->
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.4.1</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<!-- oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!--<scope>provided</scope>-->
</dependency>

<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.4.0-b180830.0359</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

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

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

</project>


application.yml


server:
port: 9000
servlet:
context-path: /

spring:
ldap:
embedded:
base-dn: dc=springframework,dc=org
ldif: classpath:test-server.ldif
port: 8389
url: ldap://localhost:8389/


Package Security configuration


AuthorizationServerConfig.java 

client: henry and secret: secret and authorization grant password and refresh_token, here is official documentation provided by Spring.

package com.henry.configuration

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore


@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfig : AuthorizationServerConfigurerAdapter() {
@Autowired
@Qualifier("authenticationManagerBean")
private val authenticationManager: AuthenticationManager? = null

@Autowired
private val passwordEncoder: BCryptPasswordEncoder? = null

@Throws(Exception::class)
override fun configure(security: AuthorizationServerSecurityConfigurer) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
}

@Throws(Exception::class)
override fun configure(clients: ClientDetailsServiceConfigurer) {
clients.inMemory().withClient("henry")
.secret(passwordEncoder!!.encode("secret"))
.scopes("read", "write")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(3600)
.accessTokenValiditySeconds(3600)
}

@Throws(Exception::class)
override fun configure(endpoints: AuthorizationServerEndpointsConfigurer) {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
}

@Bean
fun tokenStore(): JwtTokenStore {
return JwtTokenStore(accessTokenConverter())
}

@Bean
fun accessTokenConverter(): JwtAccessTokenConverter {
val converter = JwtAccessTokenConverter()
converter.setSigningKey("123")
return converter
}
}

ResourceServerConfig.java 

Configurate the cors configuration and more details here official documentation provided by Spring.

package com.henry.configuration

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.oauth2.config.annotation.web.configuration.EnableResourceServer
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource


@Configuration
@EnableResourceServer
class ResourceServerConfig : ResourceServerConfigurerAdapter() {
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.cors().and().authorizeRequests().antMatchers("/webjars/**").permitAll().anyRequest().authenticated().and().cors()
.configurationSource(corsConfigurationSource())
}

@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val config = CorsConfiguration()
config.allowedOrigins = listOf("*")
config.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
config.allowCredentials = true
config.allowedHeaders = listOf("Content-Type", "Authorization")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", config)
return source
}


}

SpringSecurityConfig.java

Configurate LDAP ldif file provided by Spring and stateless.

package com.henry.configuration

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.provider.ClientDetailsService
import org.springframework.security.oauth2.provider.approval.ApprovalStore
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory
import org.springframework.security.oauth2.provider.token.TokenStore


@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
class SpringSecurityConfig : WebSecurityConfigurerAdapter() {
@Autowired
private val clientDetailsService: ClientDetailsService? = null

@Bean
@Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager {
return super.authenticationManagerBean()
}

@Throws(Exception::class)
override fun configure(auth: AuthenticationManagerBuilder) {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(BCryptPasswordEncoder())
.passwordAttribute("userPassword")
}

@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}

@Bean
@Autowired
fun userApprovalHandler(tokenStore: TokenStore?): TokenStoreUserApprovalHandler {
val handler = TokenStoreUserApprovalHandler()
handler.setTokenStore(tokenStore)
handler.setRequestFactory(DefaultOAuth2RequestFactory(clientDetailsService))
handler.setClientDetailsService(clientDetailsService)
return handler
}

@Bean
@Autowired
@Throws(Exception::class)
fun approvalStore(tokenStore: TokenStore?): ApprovalStore {
val store = TokenApprovalStore()
store.setTokenStore(tokenStore)
return store
}

@Bean
fun passwordEncoder(): BCryptPasswordEncoder {
return BCryptPasswordEncoder()
}
}

Spring Rest APIs Controller


package com.henry.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import java.security.Principal
import java.util.*


@RestController
class DefaultRestController {
@GetMapping("/principal")
fun user(principal: Principal): Principal {
return principal
}

@GetMapping("/users")
fun hello(principal: Principal): Map<String, String>? {
return Collections.singletonMap("response", "Hello ${principal.name}")
}

}

Run & Test


Run Spring Boot application with command: mvn spring-boot:run. by console, IntelliJ etc.

Credentials for test:

Basic auth:
client: henry
secret: secret

LDAP by ldif file:
user: henry
password: 123


POSTMAN

Type: Basic Auth
Username: henry
Password: secret













Body: x-www-form-urlencoded
username: henry
password: 123
grant_type: password











Get access token

POST: http://localhost:9000/oauth/token


















When we have an access token then just add headers with  Authorization Bearer {token}

GET:

http://localhost:9000/principal

















GET:

http://localhost:9000/users

















Source Code


Here on GitHub.





References.

https://www.baeldung.com/keycloak-embedded-in-spring-boot-app
https://www.baeldung.com/spring-security-oauth-jwt
https://jwt.io/introduction
https://squareball.co/blog/the-difference-between-basic-auth-and-oauth
https://www.twilio.com/docs/glossary/what-is-basic-authentication











Sunday, July 17, 2022

Apache Cassandra with Docker

For this post I get steps  from an official documentation by Apache Cassandra check the link. 

1. Install Apache Cassandra.

docker pull cassandra:latest

2. A Docker network allows us to access the container’s ports without exposing them on the host.

docker network create cassandra

docker run --rm -d --name cassandra --hostname cassandra --network cassandra cassandra

3. Create a file named data.cql. This script will create a keyspace, the layer at which Cassandra replicates its data, a table to hold the data, and insert some data into that table:

-- Create a keyspace
CREATE KEYSPACE IF NOT EXISTS test_keyspace WITH REPLICATION={ 'class' : 'SimpleStrategy', 'replication_factor' : '1' };

-- Create a table
CREATE TABLE IF NOT EXISTS test_keyspace.users (
    email varchar primary key,
    name varchar,
    age int
);

-- Insert some data
INSERT INTO test_keyspace.users
(email, name, age)
VALUES ('test@gmail.com', 'henry', 30);

INSERT INTO test_keyspace.users
(email, name, age)
VALUES ('test2@gmail.com', 'henry123', 30);


4. Load date with CQLSH

docker run --rm --network cassandra -v "YOUR_PATH/data.cql:/scripts/data.cql" 
-e CQLSH_HOST=cassandra -e CQLSH_PORT=9042 -e CQLVERSION=3.4.5 nuvo/docker-cqlsh

5. Interactive CQLSH

docker run --rm -it --network cassandra nuvo/docker-cqlsh cqlsh cassandra 9042 --cqlversion='3.4.5'

6.  Read some  data.

SELECT * FROM test_keyspace.users;











7. Clean Up

docker kill cassandra
docker network rm cassandra









References: https://cassandra.apache.org/_/quickstart.html






Saturday, July 16, 2022

Java Streams: Read files in Parallel, Sequential and Per Chunk


Parallel processing a problem in multiple cores each process running in a separate thread. however, consider the overhead. In this example it is faster to get the paths names files  in parallel than sequential, however it is faster or equal to read the content of each file by sequential than parallel. So the parallel stream will be used in specific cases.  for example when calling a function or stored procedure from databases at the same time with different parameters or something like that.

Read files in Parallel.

Parallel computing involves dividing a problem into subproblems, solving those problems simultaneously (in parallel, with each subproblem running in a separate thread), and then combining the results of the solutions to the subproblems. 

normalize() :  Return current path and remove redundant name elements.
isRegularFile():  Return true if regular file, and false is not a regular file for example a  directory.

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;

public class Demo2 {

public static void main(String[] args) {

long startTime = System.currentTimeMillis();

try {
String directory = "C:\\demo";
var files = Files.walk(Paths.get(directory))
.parallel().map(Path::normalize)
.filter(Files::isRegularFile)
.filter(path -> path.getFileName().toString().endsWith(".txt"))
.collect(Collectors.toList());

files.stream().parallel().forEach(item -> {
System.out.println(item + " " + Thread.currentThread().getName());
try {
Files.lines(Paths.get(item.toString())).parallel().forEach(val -> {
System.out.println("value -> " + val);
});
} catch (IOException e) {
throw new RuntimeException(e);
}
});

} catch (IOException e) {
throw new RuntimeException(e);
}

long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
System.out.println("millisecond "+elapsedTime);
}
}

Result this:

C:\demo\lines1.txt ForkJoinPool.commonPool-worker-1
C:\demo\lines3.txt ForkJoinPool.commonPool-worker-2
C:\demo\lines2.txt main
value -> third file2
value -> first file1
value -> second file2
value -> third file1
value -> first file2
value -> second file1
millisecond 35



Read files in Sequential.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;

public class Demo3 {

public static void main(String[] args) {

long startTime = System.currentTimeMillis();

try {
String directory = "C:\\demo";
var files = Files.walk(Paths.get(directory))
.map(Path::normalize)
.filter(Files::isRegularFile)
.filter(path -> path.getFileName().toString().endsWith(".txt"))
.collect(Collectors.toList());

files.stream().forEach(item -> {
System.out.println(item + " " + Thread.currentThread().getName());
try {
Files.lines(Paths.get(item.toString())).forEach(val -> {
System.out.println("value -> " + val);
});
} catch (IOException e) {
throw new RuntimeException(e);
}
});

} catch (Exception e) {
throw new RuntimeException(e);
}

long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
System.out.println("millisecond " + elapsedTime);
}
}

Result this:

C:\demo\lines1.txt main
value -> first file1
value -> first file2
C:\demo\lines2.txt main
value -> second file1
value -> second file2
C:\demo\lines3.txt main
value -> third file1
value -> third file2
millisecond 38



Read files Per Chunk.

It used AtomicInteger for counting the number lines in the file. Files.lines used by threads concurrently.

The file contains 10 rows then in this example  read the file per chunk 3 rows. 


import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class Demo1 {

public static void main(String[] args) {

try {

final AtomicInteger c = new AtomicInteger();
final AtomicInteger count = new AtomicInteger(1);

String fileName = "C:\\demo\\lines1.txt";
Files.lines(Paths.get(fileName))
.collect(Collectors.groupingBy(e -> c.getAndIncrement() / 3))//per 3
.forEach((fi, item) -> {
System.out.println("Chunk " + fi + " List " + item);
for (String line : item) {
System.out.println(" Number line -> " + count.getAndIncrement() + " -> " + line);
}

});

} catch (IOException e) {
e.printStackTrace();
}
}
}

Result this:

Chunk 0 List [first file1, first file2, first file3]
 Number line -> 1 -> first file1
 Number line -> 2 -> first file2
 Number line -> 3 -> first file3
Chunk 1 List [first file4, first file5, first file6]
 Number line -> 4 -> first file4
 Number line -> 5 -> first file5
 Number line -> 6 -> first file6
Chunk 2 List [first file7, first file8, first file9]
 Number line -> 7 -> first file7
 Number line -> 8 -> first file8
 Number line -> 9 -> first file9
Chunk 3 List [first file10]
 Number line -> 10 -> first file10











References:
https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html
https://www.baeldung.com/java-when-to-use-parallel-stream
https://mkyong.com/java8/java-8-parallel-streams-examples/
https://stackoverflow.com/questions/27583623/is-there-an-elegant-way-to-process-a-stream-in-chunks
https://howtodoinjava.com/java/multi-threading/atomicinteger-example/
https://www.geeksforgeeks.org/path-normalize-method-in-java-with-examples/
https://www.logicbig.com/how-to/code-snippets/jcode-java-io-files-isregularfile.html










































































































































🔐AWS → Google Cloud Workload Identity Federation with Terraform

  Eliminating Service Account Keys for Cross-Cloud Workloads Managing identities across cloud providers is often more challenging than manag...