Skip to content

Commit

Permalink
Updating sqldb-service to data-service with webflux services
Browse files Browse the repository at this point in the history
  • Loading branch information
pranav-patil authored and Pranav Patil committed Aug 27, 2018
1 parent 2785bbf commit 44625f3
Show file tree
Hide file tree
Showing 58 changed files with 924 additions and 195 deletions.
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
# Spring Micro Services Showcase
[Spring Boot](https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/) demonstrates the capabilities of spawning but micro services for all the functionality
including MVC, JPA, Spring Security.

In this showcase you'll see the following in action:
[Microservices](https://microservices.io/) is an architectural style that structures an application as a collection of loosely coupled services, where each service implements business capabilities. A microservice runs in its own process and communicates other services via HTTP API. Every microservice can be deployed, upgraded, scaled, and restarted independently of the other services in an application.
It enables continuous delivery/deployment of large, complex applications. It allows better component isolation and high resilience against component failures. Smaller components in microservices can be scaled easily to meet increasing demand for a specific component. It increases developer independence and allows parallel development across multiple smaller teams.
Microservices brings additional complexity as the developers have to mitigate fault tolerance, network latency, and deal with load balancing. Also deployment and testing of such a distributed system is complicated and tedious.

* [Zuul Server](https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html)
* [Eureka Server](https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance)
[Spring Boot](https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/) enables to spawn stand-alone, production-grade Spring-based Applications with very little configuration, hence it is widely used in micro services arena. It is preconfigured with the Spring's standard configuration and has an embedded Tomcat or Jetty to provide full fledged server functionality.
[Spring Cloud](http://projects.spring.io/spring-cloud/) framework, provides a collection of tools and solutions to some of the commonly encountered patterns when building distributed systems. It addresses solutions for some of the common problems in distributed systems including Configuration management, Service discovery, Circuit breakers and Distributed sessions.
[Docker](https://www.docker.com/) is a open platform to create, deploy, and run applications as a lightweight, portable, self-sufficient container, which can run virtually anywhere.
These tools and platforms form the foundation for spring micro services project.

To run the application:
-------------------
From the command line with Git and Gradle:
The spring micro services showcase contains the following services in action:

$ git clone https://github.com/pranav-patil/spring-microservices.git
$ cd spring-microservices
$ gradle clean build
$ java -jar config-server/build/libs/config-server-0.0.1-SNAPSHOT.jar
* [Elastic Stack](elastic-stack/README.md): ElasticSearch-Logstash-Kibana provides log storage and management.
* [Kafka Broker](kafka-broker/README.md): Kafka Message broker provides messaging capabilities to zipkin trace messages to Zipkin server.
* [Zipkin Service](zipkin-service/README.md): Zipkin enables to trace requests spanning across multiple services.

* [Config Service](config-service/README.md): Configuration service provides access to spring **application.yml** configuration files for corresponding service stored in its centralized (currently local) location.
* [Discovery Service](discovery-service/README.md): Eureka discovery service allows micro services to find and communicate with each other.
* [Authorization Service](authorization-service/README.md): Authorization service is responsible for providing OAuth2 access tokens after authentication and validating request access tokens before allowing access to the authorized services.
* [Data Service](data-service/README.md): Data service provides reactive services using Spring WebFlux to fetch various data (currently stock data).
* [Finance Service](finance-service/README.md): Finance service provides services to fetch financial data especially stock details.
* [Analytics Service](analytics-service/README.md): Analytics services consume data from various sources, mainly finance-service and data-service and provide analytical details regarding the corresponding data.
* [Monitor Service](monitor-service/README.md): Monitor service mainly gathers hystrix circuit breaker data from finance and analytics services, and displays the [Hystrix Dashboard](https://github.com/Netflix-Skunkworks/hystrix-dashboard).
16 changes: 16 additions & 0 deletions analytics-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Analytics Service
=============

Analytics service provides various services to access analytical data from diverse resources. Currently analytics services for only financial or stock data are available.
It mainly showcases [OpenFeign Clients](https://github.com/OpenFeign/feign), [Netflix Hystrix](https://github.com/Netflix/Hystrix) circuit breakers, [Spring Cloud Sleuth](https://cloud.spring.io/spring-cloud-sleuth/) with Zipkin and [Spring Webflux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) clients.

### Running the Analytics Service

The **CONFIG_SERVICE_PASSWORD** is a required parameter to run analytics-service as it enables to access analytics-service.yml configuration file from the [config-service](/../config-service/README.md).
The **ANALYTICS_SERVICE_PASSWORD** is the client secret required to access oauth2 token for client id analytics-service. Use the same **ANALYTICS_SERVICE_PASSWORD** configured in [authorization-service](/../authorization-service/README.md).
Optionally **spring.profiles.active** can be passed with value **prod** which enables logback to send all logs to [Elastic Stack](/../elastic-stack/README.md) instead of logging in the console by default.

$ java -jar analytics-service/build/libs/analytics-service-0.0.1-SNAPSHOT.jar
-DCONFIG_SERVICE_PASSWORD=xxxx
-DANALYTICS_SERVICE_PASSWORD=yyyy
-Dspring.profiles.active=prod
3 changes: 2 additions & 1 deletion analytics-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-data-rest')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.cloud:spring-cloud-starter-openfeign')
compile('org.springframework.cloud:spring-cloud-starter-oauth2')
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
Expand All @@ -58,7 +59,7 @@ dependencyManagement {
}

processResources {
filesMatching("**/bootstrap.yml") {
filesMatching("**/application.properties") {
expand(project.properties)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor;
import org.springframework.context.annotation.Bean;
Expand All @@ -15,6 +16,7 @@
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication
@EnableDiscoveryClient
Expand Down Expand Up @@ -44,4 +46,10 @@ public RequestInterceptor oauth2FeignRequestInterceptor(){
public OAuth2RestTemplate clientCredentialsRestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails());
}

@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
package com.emprovise.service.api;

import com.emprovise.service.client.FinanceServiceClient;
import com.emprovise.service.dto.StockDetail;
import com.emprovise.service.dto.StockDetailDTO;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.HystrixCommands;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

@EnableEurekaClient
@RestController
@RequestMapping("/finance")
public class FinanceController {

@Autowired
private FinanceServiceClient financeServiceClient;
@Autowired
private WebClient.Builder webClientBuilder;

@RequestMapping("/finance")
public String finance() {
StockDetailDTO stockDetailDTO = financeServiceClient.getFinanceService("MSFT", "60min");
@GetMapping("/stock/{symbol}")
public String getStock(@PathVariable String symbol) {
StockDetailDTO stockDetailDTO = financeServiceClient.getFinanceService(symbol, "60min");
Gson gson = new Gson();
return gson.toJson(stockDetailDTO);
}

@RequestMapping("/greeting/view")
@GetMapping("/stock/summary/{name}")
public Flux<StockDetail> getStockSummary(@PathVariable String name) {
Flux<StockDetail> stockDetailFlux = webClientBuilder.build()
.get().uri("http://data-service/stocks/name/{name}", name)
.retrieve().bodyToFlux(StockDetail.class);

return HystrixCommands.from(stockDetailFlux)
.fallback(Flux.just(new StockDetail()))
.commandName("getStockSummary")
.toFlux();
}

@GetMapping("/greeting")
public String greeting() {
return financeServiceClient.greeting();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.emprovise.service.sqldbservice.dto;
package com.emprovise.service.dto;

import java.util.Date;

public class StockDetails {
public class StockDetail {

private String stockName;
private Double high;
private Double low;
private Double open;
private Double close;
private Date date = new Date();
private Date date;

public StockDetails() {
public StockDetail() {
}

public String getStockName() {
Expand Down
5 changes: 5 additions & 0 deletions analytics-service/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ----------------------------------------
# Gradle Project Properties
# ----------------------------------------

info.build.version=${version?:1.0}
1 change: 0 additions & 1 deletion analytics-service/src/main/resources/bootstrap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ spring:
username: user
password: ${CONFIG_SERVICE_PASSWORD}

info.build.version: ${version?:1.0}
57 changes: 56 additions & 1 deletion authorization-service/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,59 @@
Authorization Service
=============
## OAuth 2.0

### Running the application
OAuth 2.0 is the industry-standard protocol for authorization. The OAuth 2.0 specification defines a delegation protocol that is useful for conveying authorization decisions (via a token) across a network of web-enabled applications and APIs.
OAuth 2.0 is not an authentication protocol and is not primarily used to identify a user. Although it is essential to authenticate both the user and the client in any authorization flow defined by the protocol.
There are number of grant types/methods for a client application to acquire an access token which can be used to authenticate a request to an API endpoint.
The client authentication enforces the use of the API only by known clients. On contrary, the serialized access token once generated, is not bound to a specific client directly.

## OAuth2 Roles

* **Resource owner (the User)**: An entity capable of granting access to a protected resource. Also referred as an end-user.
* **Resource server (the API server)**: The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.
* **Client**: An application making protected resource requests on behalf of the resource owner and with its authorization.
* **Authorization server**: The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.

## Grant Types

When a client application wants access to the resources of a resource owner hosted on a resource server, the client application must first obtain an authorization grant. OAuth2 provides [several authorization grants](https://alexbilbie.com/guide-to-oauth-2-grants/). Each grant type serves a different purpose and is used in a different way. Below are OAuth2 grant types:

* **Authorization Code**:
The client redirects the user to the authorization server with parameters, response_type as code, client_id, redirect_uri and scope. The authorization server validates the request and asks user to login. The user login into the authorization server and approves the client. Upon user's approval the client is redirected to the redirect URI with parameters, state and authorization code. The client then sends a POST request to the authorization server with parameters, grant_type as authorization_code, client_id, client_secret, redirect_uri and code with the authorization code from the query string. The authorization server responds with a JSON object containing token_type as Bearer, expires_in, access_token and refresh_token.

* **Implicit**:
The implicit grant is intended to be used for single page web apps which can’t keep client secret because all of the application code and storage is easily accessible. Here the authorization server returns an access token instead of an authorization code. The client redirects the user to the authorization server with parameters, response_type as token, client_id, redirect_uri and scope. The authorization server validates the request and asks user to login. The user login into the authorization server and approves the client. Upon user's approval the client is redirected to the redirect URI with parameters, containing token_type as Bearer, expires_in and access_token. It does not return a refresh token.

* **Resource Owner Password Credentials**:
It is mainly used by trusted first party clients. The client asks the user for their authorization credentials (username and password). The client then sends a POST request to authorization server with parameters, grant_type as password, client_id, client_secret, scope, username and password. The authorization server responds with a JSON object containing token_type as Bearer, expires_in, access_token and refresh_token.

* **Client Credentials**:
It is used for machine-to-machine authentication where no specific user's permission is required to access data. The client sends a POST request to the authorization server with parameters, grant_type as client_credentials, client_id, client_secret and scope. The authorization server responds with a JSON object containing token_type as Bearer, expires_in and access_token.

* **Refresh token**:
It is used to get a new access token after expiration of current access token. The client sends a POST request to the authorization server with parameters, grant_type as refresh_token, refresh_token, client_id, client_secret and scope. The authorization server will respond with a JSON object containing token_type as Bearer, expires_in, access_token and refresh_token.

### Installation and Running of PostgreSQL Server

* Download latest [Windows PostgreSQL Installer](https://www.postgresql.org/download/windows/) and follow windows installation steps.
* Alternatively, can download [Windows PostgreSQL Binary Archive](https://www.enterprisedb.com/download-postgresql-binaries) and extract the zip file. POSTGRE_SQL_HOME is the path to the unzipped PostgreSQL **pgsql** directory.
* Run the below [pg_ctl](https://www.postgresql.org/docs/9.5/static/app-pg-ctl.html) utility commands to register PostGreSQL as service in POSTGRE_SQL_HOME/pgsql/bin directory.
* Create a new user named **appuser** and new database named **testdb** using below **psql** commands. The user and password are added to auth-service.yml configuration file as spring.datasource.username (and password).
* PostGreSQL runs on default port 5432.


createuser --password postgres
pg_ctl.exe register -N postgres -U appuser -P secret -D "POSTGRE_SQL_HOME/pgsql/data"
pg_ctl.exe register -N postgres -D "POSTGRE_SQL_HOME/pgsql/data"
pg_ctl.exe -D "POSTGRE_SQL_HOME/pgsql/data" -l logfile start
psql -U postgres
psql -U appuser postgres
create user root with password 'verysecret';
alter user root createdb;
create database testdb;
\c testdb;

### Running the Authorization Service

Pass the new AUTH_SERVICE_PASSWORD, CONFIG_SERVICE_PASSWORD from the [config-service](/../config-service/README.md) to access auth-service.yml configuration file, corresponding passwords FINANCE_SERVICE_PASSWORD for finance-service and ANALYTICS_SERVICE_PASSWORD for analytics-service respectively.

Expand Down Expand Up @@ -125,3 +177,6 @@ Pass the new AUTH_SERVICE_PASSWORD, CONFIG_SERVICE_PASSWORD from the [config-ser
}
}

### Notes

* Authorization service uses [MapStruct](http://mapstruct.org/) for mapping between domain object to DTO object. MapStruct requires [mapstruct-processor](https://github.com/mapstruct/mapstruct) to be configured in gradle to generate the corresponding Mapper implementation for defined MapStruct interface. Hence it is highly recommended to **run gradle build before running authorization-service** to avoid Spring NoSuchBeanDefinitionException for MapStruct autowirings.
4 changes: 2 additions & 2 deletions authorization-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies {
runtime('net.logstash.logback:logstash-logback-encoder:5.1')
runtime('org.postgresql:postgresql:42.2.2')
testCompile('org.springframework.boot:spring-boot-starter-test')
apt "org.mapstruct:mapstruct-processor:${mapstructVersion}"
apt "org.mapstruct:mapstruct-processor:${mapstructVersion}"
}

dependencyManagement {
Expand All @@ -52,7 +52,7 @@ dependencyManagement {
}

processResources {
filesMatching("**/bootstrap.yml") {
filesMatching("**/application.properties") {
expand(project.properties)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ----------------------------------------
# Gradle Project Properties
# ----------------------------------------

info.build.version=${version?:1.0}
2 changes: 0 additions & 2 deletions authorization-service/src/main/resources/bootstrap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ spring:
fail-fast: true
username: user
password: ${CONFIG_SERVICE_PASSWORD}

info.build.version: ${version?:1.0}
4 changes: 3 additions & 1 deletion config-service/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Config Service
=============

[Configuration Service](https://cloud.spring.io/spring-cloud-config/) handles the configurations for all of the services through a simple point-to-point service call to retrieve those configurations. The configurations for micro services is stored in an environment and not as part of the project. It is a central place to manage external properties for applications across all environments. Configuration service ideally has a dedicated Git repository for the configuration of the corresponding environment (dev/test/production).

### Create your config.jks Keystore

Expand Down Expand Up @@ -33,7 +35,7 @@ http://localhost:8888/decrypt

Authorization Basic {BASE64-ENCODED user:CONFIG_SERVICE_PASSWORD}

### Running the application
### Running the Config Service

Pass the CONFIG_KEY_PASSWORD and CONFIG_KEYSTORE_PASSWORD created from the above keytool command and new CONFIG_SERVICE_PASSWORD to secure
access to shared configuration files.
Expand Down
2 changes: 1 addition & 1 deletion config-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ dependencyManagement {
}

processResources {
filesMatching("**/bootstrap.yml") {
filesMatching("**/application.properties") {
expand(project.properties)
}
}
Expand Down
5 changes: 5 additions & 0 deletions config-service/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ----------------------------------------
# Gradle Project Properties
# ----------------------------------------

info.build.version=${version?:1.0}
Loading

0 comments on commit 44625f3

Please sign in to comment.