├── .gitignore ├── Makefile ├── README.md ├── auth-service ├── .gitignore ├── Dockerfile ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── nguyenvm │ │ │ └── auth │ │ │ ├── AuthenticationServiceApplication.java │ │ │ ├── config │ │ │ ├── KafkaConfig.java │ │ │ ├── SecurityCredentialsConfig.java │ │ │ └── SleuthSpanSenderConfiguration.java │ │ │ ├── filter │ │ │ └── JwtUsernameAndPasswordAuthenticationFilter.java │ │ │ ├── model │ │ │ ├── AppUser.java │ │ │ └── UserCredentials.java │ │ │ └── service │ │ │ └── UserDetailsServiceImpl.java │ └── resources │ │ ├── bootstrap-docker.properties │ │ └── bootstrap.properties │ └── test │ └── java │ └── com │ └── nguyenvm │ └── authservice │ └── EurekaApplicationTests.java ├── common ├── .gitignore ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nguyenvm │ │ └── common │ │ ├── config │ │ └── properties │ │ │ ├── JwtConfig.java │ │ │ └── KafkaProperties.java │ │ ├── model │ │ └── kafka │ │ │ ├── JsonDeserializer.java │ │ │ └── JsonSerializer.java │ │ └── util │ │ ├── CommonConstants.java │ │ └── ContextUtil.java │ └── resources │ ├── application.properties │ └── keystore │ └── jwt.jks ├── config-server ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nguyenvm │ │ └── configserver │ │ └── ConfigServerApplication.java │ └── resources │ ├── application.properties │ ├── auth-service │ └── auth-service.properties │ ├── bootstrap.properties │ ├── common-application │ ├── application-docker.properties │ └── application.properties │ ├── eureka-server │ └── eureka-server.properties │ ├── gateway-zuul │ └── gateway-zuul.properties │ ├── hystrix-dashboard │ └── hystrix-dashboard.properties │ ├── order-service │ └── order-service.properties │ ├── stock-service │ └── stock-service.properties │ └── turbine-stream │ └── turbine-stream.properties ├── docker ├── .env ├── docker-compose.yml ├── kafka │ └── docker-compose.yml ├── rabbit-mq │ └── docker-compose.yml └── zipkin │ └── docker-compose.yml ├── docs ├── images │ ├── build-output.png │ ├── eureka-server.png │ ├── hystrix-stream.png │ ├── netflix-oss-architecture.png │ ├── netflix-oss-framework.png │ ├── zipkin-dependencies.png │ ├── zipkin-trace-failure.png │ └── zipkin-trace-success.png └── spring-microservices.postman_collection.json ├── eureka-server ├── .gitignore ├── Dockerfile ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nguyenvm │ │ └── eureka │ │ └── EurekaServerApplication.java │ └── resources │ ├── bootstrap-docker.properties │ └── bootstrap.properties ├── gateway-zuul ├── .gitignore ├── Dockerfile ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nguyenvm │ │ └── gatewayzuul │ │ ├── GatewayZuulApplication.java │ │ └── config │ │ ├── KafkaConfig.java │ │ ├── SecurityTokenConfig.java │ │ └── SleuthSpanSenderConfiguration.java │ └── resources │ ├── bootstrap-docker.properties │ └── bootstrap.properties ├── hystrix-dashboard ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nguyenvm │ │ └── hystrixdashboard │ │ └── HystrixDashboardApplication.java │ └── resources │ ├── bootstrap-docker.properties │ └── bootstrap.properties ├── order-service ├── .gitignore ├── Dockerfile ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── nguyenvm │ │ │ └── orderservice │ │ │ ├── OrderServiceApplication.java │ │ │ ├── config │ │ │ ├── AppConfig.java │ │ │ ├── KafkaConfig.java │ │ │ ├── SecurityTokenConfig.java │ │ │ ├── SleuthSpanSenderConfiguration.java │ │ │ └── filter │ │ │ │ └── JwtTokenAuthenticationFilter.java │ │ │ ├── controller │ │ │ └── OrderController.java │ │ │ ├── model │ │ │ ├── dto │ │ │ │ ├── OrderDTO.java │ │ │ │ ├── ProductDTO.java │ │ │ │ └── mapper │ │ │ │ │ └── OrderEntityToDTOMapper.java │ │ │ └── entity │ │ │ │ └── OrderEntity.java │ │ │ └── service │ │ │ ├── OrderService.java │ │ │ ├── impl │ │ │ └── OrderServiceImpl.java │ │ │ └── kafka │ │ │ └── OrderProducer.java │ └── resources │ │ ├── bootstrap-docker.properties │ │ └── bootstrap.properties │ └── test │ └── java │ └── com │ └── nguyenvm │ └── orderservice │ └── EurekaApplicationTests.java ├── pom-base ├── pom-build.xml ├── pom-cloud.xml ├── pom-service-tracking.xml └── pom-service.xml ├── pom.xml ├── scripts ├── common-module.sh ├── create-topic.sh ├── docker-setup.sh ├── kafka-topics.txt └── maven-setup.sh ├── stock-service ├── .gitignore ├── Dockerfile ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── nguyenvm │ │ │ └── stockservice │ │ │ ├── StockServiceApplication.java │ │ │ ├── config │ │ │ ├── AppConfig.java │ │ │ ├── KafkaConfig.java │ │ │ └── SleuthSpanSenderConfiguration.java │ │ │ ├── controller │ │ │ └── StockController.java │ │ │ ├── model │ │ │ ├── dto │ │ │ │ ├── OrderDTO.java │ │ │ │ ├── ProductDTO.java │ │ │ │ └── mapper │ │ │ │ │ └── ProductEntityToDTOMapper.java │ │ │ └── entity │ │ │ │ └── ProductEntity.java │ │ │ └── service │ │ │ ├── StockService.java │ │ │ ├── impl │ │ │ └── StockServiceImpl.java │ │ │ └── kafka │ │ │ └── StockConsumer.java │ └── resources │ │ ├── bootstrap-docker.properties │ │ └── bootstrap.properties │ └── test │ └── java │ └── com │ └── nguyenvm │ └── imageservice │ └── EurekaApplicationTests.java └── turbine-stream ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src └── main ├── java └── com │ └── nguyenvm │ └── turbinestraem │ └── TurbineStreamApplication.java └── resources ├── bootstrap-docker.properties └── bootstrap.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | *.iml 4 | 5 | docker/rabbit-mq/* 6 | !docker/rabbit-mq/docker-compose.yml 7 | docker/kafka/* 8 | !docker/kafka/docker-compose.yml -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Setup maven and build jar 2 | setup/all: 3 | @make setup/common 4 | @scripts/maven-setup.sh . 5 | 6 | setup/common: 7 | @scripts/common-module.sh 8 | 9 | setup/config_server: 10 | @scripts/maven-setup.sh config-server 11 | 12 | setup/eureka_server: 13 | @scripts/maven-setup.sh eureka-server 14 | 15 | setup/auth_service: 16 | @scripts/maven-setup.sh auth-service 17 | 18 | setup/gateway_zuul: 19 | @scripts/maven-setup.sh gateway-zuul 20 | 21 | setup/order_service: 22 | @scripts/maven-setup.sh order-service 23 | 24 | setup/stock_service: 25 | @scripts/maven-setup.sh stock-service 26 | 27 | setup/turbine_stream: 28 | @scripts/maven-setup.sh turbine-stream 29 | 30 | setup/hystrix_dashboard: 31 | @scripts/maven-setup.sh hystrix-dashboard 32 | 33 | # DOCKER 34 | docker/kafka/up: 35 | @scripts/docker-setup.sh docker/kafka up 36 | 37 | docker/kafka/down: 38 | @scripts/docker-setup.sh docker/kafka down 39 | 40 | docker/kafka/stop: 41 | @scripts/docker-setup.sh docker/kafka stop 42 | 43 | docker/zipkin/up: 44 | @scripts/docker-setup.sh docker/zipkin up 45 | 46 | docker/zipkin/down: 47 | @scripts/docker-setup.sh docker/zipkin down 48 | 49 | docker/zipkin/stop: 50 | @scripts/docker-setup.sh docker/zipkin stop 51 | 52 | docker/infrastructure: 53 | @make docker/kafka/up 54 | @sleep 25 55 | @scripts/create-topic.sh 56 | @make docker/zipkin/up 57 | 58 | docker/all/up: 59 | @make docker/infrastructure 60 | @scripts/docker-setup.sh docker up 61 | 62 | docker/all/down: 63 | @scripts/docker-setup.sh docker down 64 | @make docker/zipkin/down 65 | @make docker/kafka/down 66 | 67 | docker/all/stop: 68 | @scripts/docker-setup.sh docker stop 69 | @make docker/zipkin/stop 70 | @make docker/kafka/stop 71 | 72 | docker/all/reset: 73 | @make docker/infrastructure 74 | @scripts/docker-setup.sh docker reset -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building microservice using Spring Boot and Spring Cloud Netflix 2 | 3 | - [Netflix OSS framework](#netflix-oss-framework) 4 | - [Overview](#overview) 5 | - [Architecture](#architecture) 6 | - [Build And Run](#build-and-run) 7 | - [Build](#build) 8 | - [Demo](#demo) 9 | 10 | ## Netflix OSS framework 11 | 12 | ### Overview 13 | ![Netflix OSS's ecosystem](docs/images/netflix-oss-framework.png) 14 | 15 | - **Service Discovery:** Netflix Eureka 16 | - **Circuit Breaker:** Netflix Hystrix 17 | - **Gatekeeper:** Netflix Zuul 18 | - **Intelligent Routing, Load Balancing:** Netflix Ribbon 19 | - **Monitoring:** Netflix Hystrix Dashboard and Netflix Turbine 20 | - **Services Tracing:** Sleuth, Zipkin 21 | - **Centralized Configuration:** Spring Cloud Config Server 22 | - **Distributed Messaging System:** Apache Kafka 23 | 24 | ### Architecture 25 | ![Netflix OSS's architecture](docs/images/netflix-oss-architecture.png) 26 | 27 | ## Build And Run 28 | ### Build 29 | 1. `make setup/all` to setup maven and build jar for the whole project 30 | ![Build output](docs/images/build-output.png) 31 | 2. `make docker/all/up` to start all project 32 | 3. `make docker/all/stop` to stop all project 33 | 4. For Running Local: 34 | - using **IntelliJ IDEA** file -> open -> select **spring-microservices** folder 35 | - `make setup/common` to build shared **POM** and **common** module 36 | - `make docker/infrastructure` to run kafka and zipkin 37 | - Start `config-server -> eureka-server -> gateway-zuul` firstly and then other services 38 | #### [Postman data](docs/spring-microservices.postman_collection.json) 39 | 40 | ### Demo 41 | #### URL: 42 | 1. **Kafka Brokers**: localhost:19092, localhost:29092, localhost:39092 43 | 2. **Centralized Config Server**: http://localhost:8888/service-name/service-name.properties 44 | >For example: http://localhost:8888/order-service/order-service.properties will get all properties of order-service 45 | ```bash 46 | username: nguyenvm 47 | password: nguyenvm@123 48 | ``` 49 | 3. **Eureka Server**: http://localhost:8761 (service registry & service discovery) 50 | 4. **Gateway Zuul**: http://localhost:8762 51 | 5. **Authenticate**: http://localhost:8762/auth 52 | ```bash 53 | username: admin 54 | password: 12345 55 | ``` 56 | 6. **Order Service**: http://localhost:8762/order?id=1&isFallBack=false (isFallBack=true will perform fall back method) 57 | 7. **Hystrix Dashboard**: http://localhost:9898/hystrix 58 | 8. **Turbine Stream**: http://localhost:8989 or http://turbine-stream:8989 (docker env) (Stream Aggregator is used by **Hystrix Dashboard** to monitor stream) 59 | 9. **Tracing Services**: http://localhost:9411/zipkin 60 | 61 | #### Hystrix Dashboard: 62 | ![Hystrix Dashboard](docs/images/hystrix-stream.png) 63 | #### Eureka Server: 64 | ![Eureka Server](docs/images/eureka-server.png) 65 | 66 | #### Tracking Services With Zipkin: 67 | ![zipkin-dependencies](docs/images/zipkin-dependencies.png) 68 | 69 | ![zipkin-trace-success](docs/images/zipkin-trace-success.png) 70 | 71 | ![zipkin-trace-failure](docs/images/zipkin-trace-failure.png) -------------------------------------------------------------------------------- /auth-service/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .DS_Store 4 | *.iml 5 | -------------------------------------------------------------------------------- /auth-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Authentication Service" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/auth-service-0.0.1-SNAPSHOT.jar /usr/local/lib/auth-service.jar 11 | ENTRYPOINT ["java","-jar","/usr/local/lib/auth-service.jar"] -------------------------------------------------------------------------------- /auth-service/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) 9 | 10 | -------------------------------------------------------------------------------- /auth-service/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /auth-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-service-tracking 8 | 1.0.0 9 | ../pom-base/pom-service-tracking.xml 10 | 11 | 12 | com.nguyenvm 13 | auth-service 14 | 0.0.1-SNAPSHOT 15 | jar 16 | auth-service 17 | Authentication Service 18 | 19 | 20 | 21 | com.nguyenvm 22 | common 23 | 1.0.0 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-security 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/AuthenticationServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @SpringBootApplication 9 | @EnableEurekaClient 10 | @ComponentScan(basePackages = {"com.nguyenvm.common", "com.nguyenvm.auth"}) 11 | public class AuthenticationServiceApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(AuthenticationServiceApplication.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.config; 2 | 3 | import com.nguyenvm.common.config.properties.KafkaProperties; 4 | import org.apache.kafka.clients.admin.Admin; 5 | import org.apache.kafka.clients.admin.AdminClientConfig; 6 | import org.apache.kafka.clients.producer.ProducerConfig; 7 | import org.apache.kafka.common.serialization.ByteArraySerializer; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.kafka.annotation.EnableKafka; 12 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 13 | import org.springframework.kafka.core.KafkaAdmin; 14 | import org.springframework.kafka.core.KafkaTemplate; 15 | import org.springframework.kafka.core.ProducerFactory; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @Configuration 21 | @EnableKafka 22 | public class KafkaConfig { 23 | @Autowired 24 | private KafkaProperties kafkaProperties; 25 | 26 | @Bean 27 | public Map adminConfig() { 28 | Map configs = new HashMap<>(); 29 | configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 30 | 31 | return configs; 32 | } 33 | 34 | @Bean 35 | public KafkaAdmin kafkaAdmin() { 36 | return new KafkaAdmin(adminConfig()); 37 | } 38 | 39 | @Bean 40 | public Admin admin() { 41 | Admin admin = Admin.create(adminConfig()); 42 | return admin; 43 | } 44 | 45 | @Bean("zipkinProducerConfig") 46 | public Map zipkinProducerConfig() { 47 | Map configs = new HashMap<>(); 48 | configs.put(ProducerConfig.CLIENT_ID_CONFIG, kafkaProperties.getZipkinClientId()); 49 | configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 50 | configs.put(ProducerConfig.ACKS_CONFIG, kafkaProperties.getZipkinAcks()); 51 | configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 52 | configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 53 | 54 | return configs; 55 | } 56 | 57 | @Bean("zipkinProducerFactory") 58 | public ProducerFactory zipkinProducerFactory() { 59 | return new DefaultKafkaProducerFactory<>(zipkinProducerConfig()); 60 | } 61 | 62 | @Bean("zipkinKafkaTemplate") 63 | public KafkaTemplate zipkinKafkaTemplate() { 64 | return new KafkaTemplate<>(zipkinProducerFactory()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/config/SecurityCredentialsConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.config; 2 | 3 | import com.nguyenvm.auth.filter.JwtUsernameAndPasswordAuthenticationFilter; 4 | import com.nguyenvm.common.config.properties.JwtConfig; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.config.http.SessionCreationPolicy; 14 | import org.springframework.security.core.userdetails.UserDetailsService; 15 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 16 | 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.security.interfaces.RSAPrivateKey; 19 | 20 | @EnableWebSecurity // Enable security config. This annotation denotes config for spring security. 21 | public class SecurityCredentialsConfig extends WebSecurityConfigurerAdapter { 22 | @Autowired 23 | private JwtConfig jwtConfig; 24 | 25 | @Autowired 26 | @Qualifier("jwtSigningKey") 27 | private RSAPrivateKey privateKey; 28 | 29 | @Autowired 30 | private UserDetailsService userDetailsService; 31 | 32 | @Override 33 | protected void configure(HttpSecurity http) throws Exception { 34 | http 35 | .csrf().disable() 36 | // make sure we use stateless session; session won't be used to store user's state. 37 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 38 | .and() 39 | // handle an authorized attempts 40 | .exceptionHandling().authenticationEntryPoint((req, res, e) -> res.sendError(HttpServletResponse.SC_UNAUTHORIZED)) 41 | .and() 42 | // Add a filter to validate user credentials and add token in the response header 43 | // What's the authenticationManager()? 44 | // An object provided by WebSecurityConfigurerAdapter, used to authenticate the user passing user's credentials 45 | // The filter needs this auth manager to authenticate the user. 46 | .addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, privateKey)) 47 | .authorizeRequests() 48 | // allow all POST requests 49 | .antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll() 50 | // any other requests must be authenticated 51 | .anyRequest().authenticated(); 52 | } 53 | 54 | // Spring has UserDetailsService interface, which can be overriden to provide our implementation for fetching user from database (or any other source). 55 | // The UserDetailsService object is used by the auth manager to load the user from database. // In addition, we need to define the password encoder also. So, auth manager can compare and verify passwords. 56 | @Override 57 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 58 | auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); 59 | } 60 | 61 | @Bean 62 | public BCryptPasswordEncoder passwordEncoder() { 63 | return new BCryptPasswordEncoder(4); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/config/SleuthSpanSenderConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.config; 2 | 3 | import com.nguyenvm.common.util.CommonConstants; 4 | import org.apache.kafka.clients.producer.ProducerRecord; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.cloud.sleuth.autoconfig.zipkin2.ZipkinAutoConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import zipkin2.Call; 12 | import zipkin2.Span; 13 | import zipkin2.codec.Encoding; 14 | import zipkin2.reporter.AsyncReporter; 15 | import zipkin2.reporter.BytesMessageEncoder; 16 | import zipkin2.reporter.Reporter; 17 | import zipkin2.reporter.Sender; 18 | 19 | import java.util.List; 20 | 21 | @Configuration 22 | public class SleuthSpanSenderConfiguration { 23 | @Autowired 24 | @Qualifier("zipkinKafkaTemplate") 25 | private KafkaTemplate kafkaTemplate; 26 | 27 | @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) 28 | Reporter myReporter() { 29 | return AsyncReporter.create(mySender()); 30 | } 31 | 32 | @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) 33 | MySender mySender() { 34 | return new MySender(); 35 | } 36 | 37 | public class MySender extends Sender { 38 | private BytesMessageEncoder encoder; 39 | 40 | public MySender() { 41 | this.encoder = BytesMessageEncoder.forEncoding(encoding()); 42 | } 43 | 44 | @Override 45 | public Encoding encoding() { 46 | return Encoding.JSON; 47 | } 48 | 49 | @Override 50 | public int messageMaxBytes() { 51 | return Integer.MAX_VALUE; 52 | } 53 | 54 | @Override 55 | public int messageSizeInBytes(List encodedSpans) { 56 | return encoding().listSizeInBytes(encodedSpans); 57 | } 58 | 59 | @Override 60 | public Call sendSpans(List encodedSpans) { 61 | byte[] encodedMessage = encoder.encode(encodedSpans); 62 | kafkaTemplate.send(new ProducerRecord(CommonConstants.ZIPKIN_TOPIC, encodedMessage)); 63 | 64 | return Call.create(null); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/filter/JwtUsernameAndPasswordAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.filter; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.nguyenvm.auth.model.UserCredentials; 5 | import com.nguyenvm.common.config.properties.JwtConfig; 6 | import io.jsonwebtoken.Jwts; 7 | import org.springframework.security.authentication.AuthenticationManager; 8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.AuthenticationException; 11 | import org.springframework.security.core.GrantedAuthority; 12 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 13 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 14 | 15 | import javax.servlet.FilterChain; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.IOException; 20 | import java.io.PrintWriter; 21 | import java.security.interfaces.RSAPrivateKey; 22 | import java.util.Collections; 23 | import java.util.Date; 24 | import java.util.stream.Collectors; 25 | 26 | public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 27 | // We use auth manager to validate the user credentials 28 | private AuthenticationManager authManager; 29 | 30 | private final JwtConfig jwtConfig; 31 | private final RSAPrivateKey rsaPrivateKey; 32 | 33 | public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authManager, JwtConfig jwtConfig, RSAPrivateKey rsaPrivateKey) { 34 | this.authManager = authManager; 35 | this.jwtConfig = jwtConfig; 36 | this.rsaPrivateKey = rsaPrivateKey; 37 | 38 | // By default, UsernamePasswordAuthenticationFilter listens to "/login" path. 39 | // In our case, we use "/auth". So, we need to override the defaults. 40 | this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(jwtConfig.getUri(), "POST")); 41 | } 42 | 43 | @Override 44 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 45 | try { 46 | // 1. Get credentials from request 47 | UserCredentials userCredentials = new ObjectMapper().readValue(request.getInputStream(), UserCredentials.class); 48 | 49 | // 2. Create auth object (contains credentials) which will be used by auth manager 50 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( 51 | userCredentials.getUsername(), 52 | userCredentials.getPassword(), 53 | Collections.emptyList() 54 | ); 55 | 56 | // 3. Authentication manager authenticate the user, and use UserDetailsServiceImpl::loadUserByUsername() method to load the user. 57 | return authManager.authenticate(authenticationToken); 58 | } catch (IOException e) { 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | 63 | // Upon successful authentication, generate a token. 64 | // The 'auth' passed to successfulAuthentication() is the current authenticated user 65 | @Override 66 | protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, 67 | Authentication auth) throws IOException, ServletException { 68 | Long now = System.currentTimeMillis(); 69 | String token = Jwts.builder().setSubject(auth.getName()) 70 | // Convert to list of strings. 71 | // This is important because it affects the way we get them back in the Gateway 72 | .claim("authorities", auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList())) 73 | .setIssuedAt(new Date(now)) 74 | .setExpiration(new Date(now + jwtConfig.getExpiration() * 1000)) // in milliseconds 75 | .signWith(rsaPrivateKey) 76 | .compact(); 77 | 78 | // Add token to header 79 | response.addHeader(jwtConfig.getHeader(), jwtConfig.getPrefix() + token); 80 | 81 | // return body 82 | PrintWriter out = response.getWriter(); 83 | response.setContentType("application/json"); 84 | response.setCharacterEncoding("UTF-8"); 85 | out.print(token); 86 | out.flush(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/model/AppUser.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | // A (temporary) class represent the user saved in the database. 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | public class AppUser { 12 | private Integer id; 13 | private String username, password; 14 | private String role; 15 | } 16 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/model/UserCredentials.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | // A (temporary) class just to represent the user credentials 7 | @Getter 8 | @Setter 9 | public class UserCredentials { 10 | private String username, password; 11 | } 12 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/nguyenvm/auth/service/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.auth.service; 2 | 3 | import com.nguyenvm.auth.model.AppUser; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.security.core.authority.AuthorityUtils; 7 | import org.springframework.security.core.userdetails.User; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Objects; 18 | import java.util.function.Function; 19 | import java.util.stream.Collectors; 20 | 21 | @Service 22 | public class UserDetailsServiceImpl implements UserDetailsService { 23 | @Autowired 24 | private BCryptPasswordEncoder bCryptPasswordEncoder; 25 | 26 | @Override 27 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 28 | // hard coding the users. All passwords must be encoded. 29 | final List users = Arrays.asList( 30 | new AppUser(1, "vanminhnguyenbmt", bCryptPasswordEncoder.encode("12345"), "USER"), 31 | new AppUser(2, "admin", bCryptPasswordEncoder.encode("12345"), "ADMIN") 32 | ); 33 | 34 | Map userMap = users.stream().collect(Collectors.toMap(AppUser::getUsername, Function.identity())); 35 | 36 | AppUser appUser = userMap.get(username); 37 | if (Objects.nonNull(appUser)) { 38 | // Remember that Spring needs roles to be in this format: "ROLE_" + userRole (i.e. "ROLE_ADMIN") 39 | // So, we need to set it to that format, so we can verify and compare roles (i.e. hasRole("ADMIN")). 40 | List grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_" + appUser.getRole()); 41 | 42 | // The "User" class is provided by Spring and represents a model class for user to be returned by UserDetailsService 43 | // And used by auth manager to verify and check user authentication. 44 | return new User(appUser.getUsername(), appUser.getPassword(), grantedAuthorities); 45 | } 46 | 47 | // If user not found. Throw this exception. 48 | throw new UsernameNotFoundException("Username: " + username + " not found"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /auth-service/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /auth-service/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=auth-service 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 -------------------------------------------------------------------------------- /auth-service/src/test/java/com/nguyenvm/authservice/EurekaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.authservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class EurekaApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /common/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .DS_Store 4 | *.iml 5 | -------------------------------------------------------------------------------- /common/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) 9 | 10 | -------------------------------------------------------------------------------- /common/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-build 8 | 1.0.0 9 | ../pom-base/pom-build.xml 10 | 11 | 12 | com.nguyenvm 13 | common 14 | 1.0.0 15 | jar 16 | common 17 | App common 18 | 19 | 20 | 21 | org.apache.kafka 22 | kafka-clients 23 | 2.7.1 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /common/src/main/java/com/nguyenvm/common/config/properties/JwtConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.common.config.properties; 2 | 3 | import lombok.Getter; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.security.*; 12 | import java.security.cert.Certificate; 13 | import java.security.cert.CertificateException; 14 | import java.security.interfaces.RSAPrivateKey; 15 | import java.security.interfaces.RSAPublicKey; 16 | 17 | @Configuration 18 | @Getter 19 | @Slf4j 20 | public class JwtConfig { 21 | @Value("${security.jwt.uri:/auth/**}") 22 | private String uri; 23 | 24 | @Value("${security.jwt.header:Authorization}") 25 | private String header; 26 | 27 | @Value("${security.jwt.prefix:Bearer }") 28 | private String prefix; 29 | 30 | @Value("${security.jwt.expiration:#{24*60*60}}") 31 | private int expiration; 32 | 33 | @Value("${security.jwt.keystore-location:}") 34 | private String keyStorePath; 35 | 36 | @Value("${security.jwt.key-alias:}") 37 | private String keyAlias; 38 | 39 | @Value("${security.jwt.keystore-password:}") 40 | private String keyStorePassword; 41 | 42 | @Bean 43 | public KeyStore keyStore() { 44 | try (InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(keyStorePath)) { 45 | KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 46 | keyStore.load(resourceAsStream, keyStorePassword.toCharArray()); 47 | return keyStore; 48 | } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) { 49 | log.error("Unable to load keystore: {}", keyStorePath, e); 50 | } 51 | 52 | throw new IllegalArgumentException("Unable to load keystore"); 53 | } 54 | 55 | @Bean 56 | public RSAPrivateKey jwtSigningKey(KeyStore keyStore) { 57 | try { 58 | Key key = keyStore.getKey(keyAlias, keyStorePassword.toCharArray()); 59 | if (key instanceof RSAPrivateKey) { 60 | return (RSAPrivateKey) key; 61 | } 62 | } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { 63 | log.error("Unable to load private key from keystore: {}", keyStorePath, e); 64 | } 65 | 66 | throw new IllegalArgumentException("Unable to load private key"); 67 | } 68 | 69 | @Bean 70 | public RSAPublicKey jwtValidationKey(KeyStore keyStore) { 71 | try { 72 | Certificate certificate = keyStore.getCertificate(keyAlias); 73 | PublicKey publicKey = certificate.getPublicKey(); 74 | 75 | if (publicKey instanceof RSAPublicKey) { 76 | return (RSAPublicKey) publicKey; 77 | } 78 | } catch (KeyStoreException e) { 79 | log.error("Unable to load private key from keystore: {}", keyStorePath, e); 80 | } 81 | 82 | throw new IllegalArgumentException("Unable to load RSA public key"); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /common/src/main/java/com/nguyenvm/common/config/properties/KafkaProperties.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.common.config.properties; 2 | 3 | import lombok.Getter; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | @Getter 9 | public class KafkaProperties { 10 | @Value("${kafka.client.id:}") 11 | private String clientId; 12 | 13 | @Value("${spring.cloud.stream.kafka.binder.brokers:}") 14 | private String bootstrapServers; 15 | 16 | @Value("${kafka.buffer.memory:}") 17 | private String bufferMemory; 18 | 19 | @Value("${kafka.retries:}") 20 | private Integer retries; 21 | 22 | @Value("${kafka.max.request.size:}") 23 | private Integer maxRequestSize; 24 | 25 | @Value("${kafka.max.partition.fetch.bytes:}") 26 | private Integer maxPartitionFetchByte; 27 | 28 | @Value("${kafka.transactional.id:}") 29 | private String transactionId; 30 | 31 | @Value("${kafka.group.id:}") 32 | private String groupId; 33 | 34 | @Value("${kafka.auto.create.topics:}") 35 | private Boolean autoCreateTopic; 36 | 37 | @Value("zipkin-service") 38 | private String zipkinClientId; 39 | 40 | @Value("0") 41 | private String zipkinAcks; 42 | } -------------------------------------------------------------------------------- /common/src/main/java/com/nguyenvm/common/model/kafka/JsonDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.common.model.kafka; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.nguyenvm.common.util.ContextUtil; 5 | import org.apache.kafka.common.errors.SerializationException; 6 | import org.apache.kafka.common.serialization.Deserializer; 7 | 8 | import java.util.Map; 9 | import java.util.Objects; 10 | 11 | public class JsonDeserializer implements Deserializer { 12 | public static final String VALUE_CLASS_NAME_CONFIG = "value.class.name"; 13 | private Class className; 14 | 15 | @Override 16 | public void configure(Map props, boolean isKey) { 17 | className = (Class) props.get(VALUE_CLASS_NAME_CONFIG); 18 | } 19 | 20 | @Override 21 | public T deserialize(String topic, byte[] bytes) { 22 | if (Objects.isNull(bytes)) return null; 23 | 24 | try { 25 | return ContextUtil.getBean(ObjectMapper.class).readValue(bytes, className); 26 | } catch (Exception e) { 27 | throw new SerializationException("Error deserializing JSON message", e); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/com/nguyenvm/common/model/kafka/JsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.common.model.kafka; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.nguyenvm.common.util.ContextUtil; 6 | import org.apache.kafka.common.errors.SerializationException; 7 | import org.apache.kafka.common.serialization.Serializer; 8 | 9 | import java.util.Objects; 10 | 11 | public class JsonSerializer implements Serializer { 12 | @Override 13 | public byte[] serialize(String topic, T data) { 14 | if (Objects.isNull(data)) return null; 15 | 16 | try { 17 | return ContextUtil.getBean(ObjectMapper.class).writeValueAsString(data).getBytes(); 18 | } catch (JsonProcessingException e) { 19 | throw new SerializationException("Error serializing JSON message", e); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/com/nguyenvm/common/util/CommonConstants.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.common.util; 2 | 3 | public final class CommonConstants { 4 | public final static String STOCK_SERVICE_URL = "http://stock-service/stock/product/getAll?isFallBack="; 5 | 6 | // Kafka 7 | public final static String ORDER_TOPIC = "order-topic"; 8 | public final static String ZIPKIN_TOPIC = "zipkin"; 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/java/com/nguyenvm/common/util/ContextUtil.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.common.util; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class ContextUtil { 9 | private static ApplicationContext applicationContext; 10 | 11 | @Autowired 12 | protected ContextUtil(ApplicationContext applicationContext) { 13 | ContextUtil.applicationContext = applicationContext; 14 | } 15 | 16 | /** 17 | * Get a bean by Class from application context 18 | * 19 | * @param clazz 20 | * @return bean 21 | */ 22 | public static T getBean(Class clazz) { 23 | return applicationContext.getBean(clazz); 24 | } 25 | 26 | /** 27 | * Get a bean by bean name from application context 28 | * 29 | * @param beanName 30 | * @return bean 31 | */ 32 | public static Object getBean(String beanName) { 33 | return applicationContext.getBean(beanName); 34 | } 35 | } -------------------------------------------------------------------------------- /common/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # JWT 2 | security.jwt.uri=/auth/** 3 | security.jwt.header=Authorization 4 | security.jwt.prefix=Bearer 5 | security.jwt.keystore-location=keystore/jwt.jks 6 | security.jwt.key-alias=nguyenvm 7 | security.jwt.keystore-password=nguyenvm@123 8 | -------------------------------------------------------------------------------- /common/src/main/resources/keystore/jwt.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/common/src/main/resources/keystore/jwt.jks -------------------------------------------------------------------------------- /config-server/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /config-server/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /config-server/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/config-server/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /config-server/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /config-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Config Server" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/config-server-0.0.1-SNAPSHOT.jar /usr/local/lib/config-server.jar 11 | EXPOSE 8888 12 | ENTRYPOINT ["java","-jar","/usr/local/lib/config-server.jar"] -------------------------------------------------------------------------------- /config-server/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /config-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-cloud 8 | 1.0.0 9 | ../pom-base/pom-cloud.xml 10 | 11 | 12 | com.nguyenvm 13 | config-server 14 | 0.0.1-SNAPSHOT 15 | jar 16 | config-server 17 | Configuration Server 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-config-server 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-security 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-maven-plugin 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /config-server/src/main/java/com/nguyenvm/configserver/ConfigServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.configserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.config.server.EnableConfigServer; 6 | 7 | @EnableConfigServer 8 | @SpringBootApplication 9 | public class ConfigServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ConfigServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /config-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=native 2 | server.port=8888 3 | 4 | spring.security.user.name=${CONFIG_SERVER_USERNAME} 5 | spring.security.user.password=${CONFIG_SERVER_PASSWORD} -------------------------------------------------------------------------------- /config-server/src/main/resources/auth-service/auth-service.properties: -------------------------------------------------------------------------------- 1 | # service name 2 | spring.application.name=auth-service 3 | 4 | # port 5 | server.port=9100 6 | 7 | # Kafka 8 | kafka.client.id=auth-service -------------------------------------------------------------------------------- /config-server/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.server.native.search-locations=\ 2 | classpath:/common-application, \ 3 | classpath:/eureka-server, \ 4 | classpath:/gateway-zuul, \ 5 | classpath:/auth-service, \ 6 | classpath:/order-service, \ 7 | classpath:/stock-service, \ 8 | classpath:/hystrix-dashboard, \ 9 | classpath:/turbine-stream 10 | 11 | 12 | #management.endpoints.web.exposure.include=* -------------------------------------------------------------------------------- /config-server/src/main/resources/common-application/application-docker.properties: -------------------------------------------------------------------------------- 1 | eureka.client.serviceUrl.zone1=http://eureka-server:8761/eureka/ 2 | spring.cloud.stream.kafka.binder.brokers=kafka-1:9092,kafka-2:9092,kafka-3:9092 3 | spring.zipkin.baseUrl=http://zipkin:9411/ -------------------------------------------------------------------------------- /config-server/src/main/resources/common-application/application.properties: -------------------------------------------------------------------------------- 1 | # eureka server url 2 | eureka.instance.prefer-ip-address=true 3 | eureka.instance.metadata-map.zone=zone1 4 | eureka.client.service-url.zone1=http://localhost:8761/eureka/ 5 | eureka.client.register-with-eureka=true 6 | eureka.client.fetch-registry=true 7 | eureka.client.region=region1 8 | eureka.client.availability-zones.region1=zone1 9 | 10 | # Kafka 11 | spring.cloud.stream.kafka.binder.brokers=localhost:19092,localhost:29092,localhost:39092 12 | spring.cloud.stream.kafka.binder.auto-create-topics=false 13 | # 64 * 1024 * 1024l 14 | kafka.buffer.memory=67108864 15 | kafka.retries=10 16 | 17 | # Zipkin, Sleuth 18 | spring.zipkin.baseUrl=http://localhost:9411/ 19 | spring.zipkin.sender.type=kafka 20 | spring.sleuth.sampler.probability=1 21 | spring.sleuth.web.skipPattern=org.springframework.cloud.netflix.hystrix.stream.HystrixStreamTask 22 | spring.sleuth.scheduled.skipPattern=org.springframework.cloud.netflix.hystrix.stream.HystrixStreamTask -------------------------------------------------------------------------------- /config-server/src/main/resources/eureka-server/eureka-server.properties: -------------------------------------------------------------------------------- 1 | # Give a name to the eureka server 2 | spring.application.name=eureka-server 3 | 4 | # default port for eureka server 5 | server.port=8761 6 | eureka.instance.hostname=eureka-server 7 | eureka.instance.prefer-ip-address=false 8 | 9 | # eureka by default will register itself as a client. So, we need to set it to false. 10 | # What's a client server? See other microservices (stock-service, order-service, auth-service, etc). 11 | eureka.client.register-with-eureka=false 12 | eureka.client.fetch-registry=false -------------------------------------------------------------------------------- /config-server/src/main/resources/gateway-zuul/gateway-zuul.properties: -------------------------------------------------------------------------------- 1 | # service name 2 | spring.application.name=gateway-zuul 3 | 4 | # port 5 | server.port=8762 6 | 7 | # A prefix that can added to beginning of all requests. 8 | #zuul.prefix=/api 9 | 10 | # Disable accessing services using service name (i.e. order-service). 11 | # They should be only accessed through the path defined below. 12 | zuul.ignored-services=* 13 | 14 | # Map path to auth service 15 | zuul.routes.auth-service.path=/auth/** 16 | zuul.routes.auth-service.service-id=auth-service 17 | 18 | # By default, all requests to order-service for example will start with: "/order/" 19 | # What will be sent to the order service is what comes after the path defined, 20 | # So, if request is "/order/view/1", order-service will get "/view/1". 21 | # In case of auth, we need to pass the "/auth/" in the path to auth service. So, set strip-prefix to false 22 | zuul.routes.auth-service.strip-prefix=false 23 | 24 | # Exclude authorization from sensitive headers 25 | zuul.routes.auth-service.sensitive-headers=Cookie,Set-Cookie 26 | 27 | # Map paths to services 28 | zuul.routes.order-service.path=/order/** 29 | zuul.routes.order-service.service-id=order-service 30 | zuul.routes.order-service.strip-prefix=false 31 | zuul.routes.order-service.sensitive-headers=Cookie,Set-Cookie 32 | 33 | # Increase the Hystrix timeout to 10s (globally) 34 | hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=20000 35 | 36 | ribbon.ReadTimeout=5000 37 | ribbon.ConnectTimeout=5000 38 | zuul.ribbon.eager-load.enabled=true 39 | 40 | # Kafka 41 | kafka.client.id=auth-service 42 | 43 | # Others 44 | management.endpoint.hystrix.stream.enabled=false -------------------------------------------------------------------------------- /config-server/src/main/resources/hystrix-dashboard/hystrix-dashboard.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=hystrix-dashboard 2 | server.port=9898 3 | hystrix.dashboard.proxy-stream-allow-list=* -------------------------------------------------------------------------------- /config-server/src/main/resources/order-service/order-service.properties: -------------------------------------------------------------------------------- 1 | # service name 2 | spring.application.name=order-service 3 | 4 | # port 5 | server.port=8200 6 | 7 | # Kafka 8 | kafka.client.id=order-service 9 | kafka.transactional.id=order-transaction 10 | kafka.group.id=order-group 11 | kafka.auto.create.topics=false 12 | # 6 * 1024 * 1024l 13 | kafka.max.request.size=6291456 14 | 15 | # Others 16 | management.endpoints.web.exposure.include=hystrix.stream -------------------------------------------------------------------------------- /config-server/src/main/resources/stock-service/stock-service.properties: -------------------------------------------------------------------------------- 1 | # service name 2 | spring.application.name=stock-service 3 | 4 | # port 5 | server.port=8100 6 | 7 | # Kafka 8 | kafka.client.id=stock-service 9 | kafka.transactional.id=stock-transaction 10 | kafka.group.id=stock-group 11 | kafka.auto.create.topics=false 12 | # 6 * 1024 * 1024l 13 | kafka.max.request.size=6291456 14 | 15 | # Others 16 | management.endpoints.web.exposure.include=hystrix.stream -------------------------------------------------------------------------------- /config-server/src/main/resources/turbine-stream/turbine-stream.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=turbine-stream 2 | server.port=8989 3 | 4 | management.endpoints.web.exposure.include=* 5 | turbine.stream.destination=hystrixStreamOutput 6 | 7 | eureka.client.register-with-eureka=false 8 | eureka.client.fetch-registry=false -------------------------------------------------------------------------------- /docker/.env: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_USERNAME=nguyenvm 2 | CONFIG_SERVER_PASSWORD=nguyenvm@123 -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | ## Start - Config server definition 5 | config-server: 6 | container_name: config-server 7 | build: 8 | context: ../config-server 9 | environment: 10 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 11 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 12 | ports: 13 | - "8888:8888" 14 | ## End - Config server definition 15 | 16 | ## Start - Eureka server definition 17 | eureka-server: 18 | container_name: eureka-server 19 | build: 20 | context: ../eureka-server 21 | environment: 22 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 23 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 24 | SPRING_PROFILES_ACTIVE: docker 25 | ports: 26 | - "8761:8761" 27 | depends_on: 28 | - config-server 29 | restart: on-failure 30 | ## End - Eureka server definition 31 | 32 | ## Start - Gateway zuul definition 33 | gateway-zuul: 34 | container_name: gateway-zuul 35 | build: 36 | context: ../gateway-zuul 37 | environment: 38 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 39 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 40 | SPRING_PROFILES_ACTIVE: docker 41 | ports: 42 | - "8762:8762" 43 | depends_on: 44 | - config-server 45 | - eureka-server 46 | restart: on-failure 47 | ## End - Gateway zuul definition 48 | 49 | ## Start - Authenticate service definition 50 | auth-service: 51 | container_name: auth-service 52 | build: 53 | context: ../auth-service 54 | environment: 55 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 56 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 57 | SPRING_PROFILES_ACTIVE: docker 58 | ports: 59 | - "9100:9100" 60 | depends_on: 61 | - config-server 62 | - eureka-server 63 | restart: on-failure 64 | ## End - Authenticate service definition 65 | 66 | ## Start - Order service definition 67 | order-service: 68 | container_name: order-service 69 | build: 70 | context: ../order-service 71 | environment: 72 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 73 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 74 | SPRING_PROFILES_ACTIVE: docker 75 | ports: 76 | - "8200:8200" 77 | depends_on: 78 | - config-server 79 | - eureka-server 80 | restart: on-failure 81 | ## End - Order service definition 82 | 83 | ## Start - Stock service definition 84 | stock-service: 85 | container_name: stock-service 86 | build: 87 | context: ../stock-service 88 | environment: 89 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 90 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 91 | SPRING_PROFILES_ACTIVE: docker 92 | ports: 93 | - "8100:8100" 94 | depends_on: 95 | - config-server 96 | - eureka-server 97 | restart: on-failure 98 | ## End - Stock service definition 99 | 100 | ## Start - Turbine stream definition 101 | turbine-stream: 102 | container_name: turbine-stream 103 | build: 104 | context: ../turbine-stream 105 | environment: 106 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 107 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 108 | SPRING_PROFILES_ACTIVE: docker 109 | ports: 110 | - "8989:8989" 111 | depends_on: 112 | - config-server 113 | restart: on-failure 114 | ## End - Turbine stream definition 115 | 116 | ## Start - Hystrix dashboard definition 117 | hytrix-dashboard: 118 | container_name: hytrix-dashboard 119 | build: 120 | context: ../hystrix-dashboard 121 | environment: 122 | CONFIG_SERVER_USERNAME: $CONFIG_SERVER_USERNAME 123 | CONFIG_SERVER_PASSWORD: $CONFIG_SERVER_PASSWORD 124 | SPRING_PROFILES_ACTIVE: docker 125 | ports: 126 | - "9898:9898" 127 | depends_on: 128 | - config-server 129 | restart: on-failure 130 | ## End - Hystrix dashboard definition 131 | 132 | networks: 133 | default: 134 | external: true 135 | name: kafka_default -------------------------------------------------------------------------------- /docker/kafka/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | zookeeper: 5 | image: confluentinc/cp-zookeeper:6.2.1 6 | container_name: zookeeper 7 | ports: 8 | - "2181:2181" 9 | environment: 10 | ZOOKEEPER_CLIENT_PORT: 2181 11 | ZOOKEEPER_TICK_TIME: 2000 12 | volumes: 13 | - ./zookeeper/data:/var/lib/zookeeper/data 14 | - ./zookeeper/log:/var/lib/zookeeper/log 15 | 16 | kafka-1: 17 | image: confluentinc/cp-kafka:6.2.1 18 | container_name: kafka-1 19 | ports: 20 | # To learn about configuring Kafka for access across networks see 21 | # https://www.confluent.io/blog/kafka-client-cannot-connect-to-broker-on-aws-on-docker-etc/ 22 | - "19092:19092" 23 | depends_on: 24 | - zookeeper 25 | environment: 26 | KAFKA_BROKER_ID: 1 27 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 28 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 29 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-1:9092,PLAINTEXT_HOST://localhost:19092 30 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 31 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 32 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 33 | volumes: 34 | - ./kafka-1/data:/var/lib/kafka/data 35 | 36 | kafka-2: 37 | image: confluentinc/cp-kafka:6.2.1 38 | container_name: kafka-2 39 | ports: 40 | # To learn about configuring Kafka for access across networks see 41 | # https://www.confluent.io/blog/kafka-client-cannot-connect-to-broker-on-aws-on-docker-etc/ 42 | - "29092:29092" 43 | depends_on: 44 | - zookeeper 45 | environment: 46 | KAFKA_BROKER_ID: 2 47 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 48 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 49 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-2:9092,PLAINTEXT_HOST://localhost:29092 50 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 51 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 52 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 53 | volumes: 54 | - ./kafka-2/data:/var/lib/kafka/data 55 | 56 | kafka-3: 57 | image: confluentinc/cp-kafka:6.2.1 58 | container_name: kafka-3 59 | ports: 60 | # To learn about configuring Kafka for access across networks see 61 | # https://www.confluent.io/blog/kafka-client-cannot-connect-to-broker-on-aws-on-docker-etc/ 62 | - "39092:39092" 63 | depends_on: 64 | - zookeeper 65 | environment: 66 | KAFKA_BROKER_ID: 3 67 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 68 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 69 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-3:9092,PLAINTEXT_HOST://localhost:39092 70 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 71 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 72 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 73 | volumes: 74 | - ./kafka-3/data:/var/lib/kafka/data -------------------------------------------------------------------------------- /docker/rabbit-mq/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | rabbit-mq: 5 | image: rabbitmq:3-management 6 | container_name: local-rabbitmq 7 | environment: 8 | RABBITMQ_DEFAULT_USER: nguyenvm 9 | RABBITMQ_DEFAULT_PASS: nguyenvm@123 10 | ports: 11 | - "15672:15672" 12 | volumes: 13 | - ./docker-conf/rabbitmq/data/:/var/lib/rabbitmq/ 14 | - ./docker-conf/rabbitmq/log/:/var/log/rabbitmq -------------------------------------------------------------------------------- /docker/zipkin/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | zipkin: 5 | image: ghcr.io/openzipkin/zipkin:${TAG:-latest} 6 | container_name: zipkin 7 | ports: 8 | - "9411:9411" 9 | environment: 10 | KAFKA_BOOTSTRAP_SERVERS: kafka-1:9092,kafka-2:9092,kafka-3:9092 11 | 12 | networks: 13 | default: 14 | external: true 15 | name: kafka_default 16 | -------------------------------------------------------------------------------- /docs/images/build-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/build-output.png -------------------------------------------------------------------------------- /docs/images/eureka-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/eureka-server.png -------------------------------------------------------------------------------- /docs/images/hystrix-stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/hystrix-stream.png -------------------------------------------------------------------------------- /docs/images/netflix-oss-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/netflix-oss-architecture.png -------------------------------------------------------------------------------- /docs/images/netflix-oss-framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/netflix-oss-framework.png -------------------------------------------------------------------------------- /docs/images/zipkin-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/zipkin-dependencies.png -------------------------------------------------------------------------------- /docs/images/zipkin-trace-failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/zipkin-trace-failure.png -------------------------------------------------------------------------------- /docs/images/zipkin-trace-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/docs/images/zipkin-trace-success.png -------------------------------------------------------------------------------- /docs/spring-microservices.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "e99ef3bc-e222-490c-890d-6241eb722bd6", 4 | "name": "spring-microservices", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "auth", 10 | "request": { 11 | "method": "POST", 12 | "header": [], 13 | "body": { 14 | "mode": "raw", 15 | "raw": "{\n \"username\": \"admin\",\n \"password\": \"12345\"\n}", 16 | "options": { 17 | "raw": { 18 | "language": "json" 19 | } 20 | } 21 | }, 22 | "url": { 23 | "raw": "http://localhost:8762/auth", 24 | "protocol": "http", 25 | "host": [ 26 | "localhost" 27 | ], 28 | "port": "8762", 29 | "path": [ 30 | "auth" 31 | ] 32 | } 33 | }, 34 | "response": [] 35 | }, 36 | { 37 | "name": "order", 38 | "request": { 39 | "auth": { 40 | "type": "bearer", 41 | "bearer": [ 42 | { 43 | "key": "token", 44 | "value": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwiaWF0IjoxNjM1ODQwOTUxLCJleHAiOjE2MzU5MjczNTF9.ne8jwC9s7axdotGmdEN86SjntBdw8gAB1Fa9MkWAjnsPpuWh0MATbqBxSZ5nwzQQyRLJrwrzatyeyvmmG5uVSEqzr-ELJeLXi8ZZX6wgvJouzEMM8T0bb0X5xVallx3klFf0WYHytErlgCw6VREnwZJtGxt7JHVL3XfiUl4ihx42cGoEd3dFRP5W8oQisd1zxJi3Swc-NCjPIFIwnFQvHrvssTIvxuuYud05pcjwO_NBgeXV2qZU1VWxKN0Fl9LnajERZgbAWkbT9Ecm1JzOIQbPe8yQdzcH5OOcGn3S9cPWny8qiSNgEa90Hydgjib0D6aQ7NANtboZVIY7JYsDTg", 45 | "type": "string" 46 | } 47 | ] 48 | }, 49 | "method": "GET", 50 | "header": [], 51 | "url": { 52 | "raw": "http://localhost:8762/order?id=1&isFallBack=false", 53 | "protocol": "http", 54 | "host": [ 55 | "localhost" 56 | ], 57 | "port": "8762", 58 | "path": [ 59 | "order" 60 | ], 61 | "query": [ 62 | { 63 | "key": "id", 64 | "value": "1" 65 | }, 66 | { 67 | "key": "isFallBack", 68 | "value": "false" 69 | } 70 | ] 71 | } 72 | }, 73 | "response": [] 74 | }, 75 | { 76 | "name": "getListProduct", 77 | "request": { 78 | "method": "GET", 79 | "header": [], 80 | "url": { 81 | "raw": "http://localhost:8100/product/getAll", 82 | "protocol": "http", 83 | "host": [ 84 | "localhost" 85 | ], 86 | "port": "8100", 87 | "path": [ 88 | "product", 89 | "getAll" 90 | ] 91 | } 92 | }, 93 | "response": [] 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /eureka-server/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .DS_Store 4 | *.iml 5 | -------------------------------------------------------------------------------- /eureka-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Eureka Server" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/eureka-server-0.0.1-SNAPSHOT.jar /usr/local/lib/eureka-server.jar 11 | EXPOSE 8761 12 | ENTRYPOINT ["java","-jar","/usr/local/lib/eureka-server.jar"] -------------------------------------------------------------------------------- /eureka-server/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) 9 | 10 | -------------------------------------------------------------------------------- /eureka-server/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /eureka-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-cloud 8 | 1.0.0 9 | ../pom-base/pom-cloud.xml 10 | 11 | 12 | com.nguyenvm 13 | eureka-server 14 | 0.0.1-SNAPSHOT 15 | jar 16 | eureka-server 17 | Eureka Server 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-netflix-eureka-server 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-maven-plugin 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /eureka-server/src/main/java/com/nguyenvm/eureka/EurekaServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class EurekaServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /eureka-server/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /eureka-server/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=eureka-server 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 -------------------------------------------------------------------------------- /gateway-zuul/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .DS_Store 4 | *.iml 5 | -------------------------------------------------------------------------------- /gateway-zuul/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Gateway Zuul" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/gateway-zuul-0.0.1-SNAPSHOT.jar /usr/local/lib/gateway-zuul.jar 11 | EXPOSE 8762 12 | ENTRYPOINT ["java","-jar","/usr/local/lib/gateway-zuul.jar"] -------------------------------------------------------------------------------- /gateway-zuul/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) 9 | 10 | -------------------------------------------------------------------------------- /gateway-zuul/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /gateway-zuul/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.10.RELEASE 9 | 10 | 11 | 12 | com.nguyenvm 13 | gateway-zuul 14 | 0.0.1-SNAPSHOT 15 | jar 16 | gateway-zuul 17 | Gateway Zuul 18 | 19 | 20 | 1.8 21 | Hoxton.SR11 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-dependencies 29 | ${spring-cloud.version} 30 | pom 31 | import 32 | 33 | 34 | 35 | 36 | 37 | 38 | com.nguyenvm 39 | common 40 | 1.0.0 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-web 46 | 2.3.10.RELEASE 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-config 52 | 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-starter-bootstrap 57 | 3.0.3 58 | 59 | 60 | 61 | org.springframework.cloud 62 | spring-cloud-starter-netflix-zuul 63 | 64 | 65 | 66 | org.springframework.cloud 67 | spring-cloud-starter-netflix-eureka-client 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-security 73 | 74 | 75 | 76 | org.springframework.cloud 77 | spring-cloud-starter-stream-kafka 78 | 79 | 80 | 81 | org.springframework.cloud 82 | spring-cloud-starter-sleuth 83 | 84 | 85 | 86 | org.springframework.cloud 87 | spring-cloud-starter-zipkin 88 | 89 | 90 | 91 | 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-maven-plugin 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /gateway-zuul/src/main/java/com/nguyenvm/gatewayzuul/GatewayZuulApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.gatewayzuul; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 7 | import org.springframework.context.annotation.ComponentScan; 8 | 9 | @SpringBootApplication 10 | @EnableEurekaClient 11 | @EnableZuulProxy 12 | @ComponentScan(basePackages = {"com.nguyenvm.common", "com.nguyenvm.gatewayzuul"}) 13 | public class GatewayZuulApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(GatewayZuulApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /gateway-zuul/src/main/java/com/nguyenvm/gatewayzuul/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.gatewayzuul.config; 2 | 3 | import com.nguyenvm.common.config.properties.KafkaProperties; 4 | import org.apache.kafka.clients.admin.Admin; 5 | import org.apache.kafka.clients.admin.AdminClientConfig; 6 | import org.apache.kafka.clients.producer.ProducerConfig; 7 | import org.apache.kafka.common.serialization.ByteArraySerializer; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.kafka.annotation.EnableKafka; 12 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 13 | import org.springframework.kafka.core.KafkaAdmin; 14 | import org.springframework.kafka.core.KafkaTemplate; 15 | import org.springframework.kafka.core.ProducerFactory; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @Configuration 21 | @EnableKafka 22 | public class KafkaConfig { 23 | @Autowired 24 | private KafkaProperties kafkaProperties; 25 | 26 | @Bean 27 | public Map adminConfig() { 28 | Map configs = new HashMap<>(); 29 | configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 30 | 31 | return configs; 32 | } 33 | 34 | @Bean 35 | public KafkaAdmin kafkaAdmin() { 36 | return new KafkaAdmin(adminConfig()); 37 | } 38 | 39 | @Bean 40 | public Admin admin() { 41 | Admin admin = Admin.create(adminConfig()); 42 | return admin; 43 | } 44 | 45 | @Bean("zipkinProducerConfig") 46 | public Map zipkinProducerConfig() { 47 | Map configs = new HashMap<>(); 48 | configs.put(ProducerConfig.CLIENT_ID_CONFIG, kafkaProperties.getZipkinClientId()); 49 | configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 50 | configs.put(ProducerConfig.ACKS_CONFIG, kafkaProperties.getZipkinAcks()); 51 | configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 52 | configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 53 | 54 | return configs; 55 | } 56 | 57 | @Bean("zipkinProducerFactory") 58 | public ProducerFactory zipkinProducerFactory() { 59 | return new DefaultKafkaProducerFactory<>(zipkinProducerConfig()); 60 | } 61 | 62 | @Bean("zipkinKafkaTemplate") 63 | public KafkaTemplate zipkinKafkaTemplate() { 64 | return new KafkaTemplate<>(zipkinProducerFactory()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /gateway-zuul/src/main/java/com/nguyenvm/gatewayzuul/config/SecurityTokenConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.gatewayzuul.config; 2 | 3 | import com.nguyenvm.common.config.properties.JwtConfig; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.http.HttpMethod; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.config.http.SessionCreationPolicy; 10 | 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | // Enable security config. This annotation denotes config for spring security. 14 | @EnableWebSecurity 15 | public class SecurityTokenConfig extends WebSecurityConfigurerAdapter { 16 | @Autowired 17 | private JwtConfig jwtConfig; 18 | 19 | @Override 20 | protected void configure(HttpSecurity http) throws Exception { 21 | http 22 | .csrf().disable() 23 | // make sure we use stateless session; session won't be used to store user's state. 24 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 25 | .and() 26 | // handle an authorized attempts 27 | .exceptionHandling().authenticationEntryPoint((req, res, e) -> res.sendError(HttpServletResponse.SC_UNAUTHORIZED)) 28 | .and() 29 | // authorization requests config 30 | .authorizeRequests() 31 | // allow all who are accessing "auth" service 32 | .antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll() 33 | // must be an admin if trying to access admin area (authentication is also required here) 34 | .antMatchers( 35 | "/actuator/**", 36 | "/order/**" 37 | ).permitAll() 38 | .anyRequest().authenticated(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /gateway-zuul/src/main/java/com/nguyenvm/gatewayzuul/config/SleuthSpanSenderConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.gatewayzuul.config; 2 | 3 | import com.nguyenvm.common.util.CommonConstants; 4 | import org.apache.kafka.clients.producer.ProducerRecord; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import zipkin2.Call; 12 | import zipkin2.Span; 13 | import zipkin2.codec.Encoding; 14 | import zipkin2.reporter.AsyncReporter; 15 | import zipkin2.reporter.BytesMessageEncoder; 16 | import zipkin2.reporter.Reporter; 17 | import zipkin2.reporter.Sender; 18 | 19 | import java.util.List; 20 | 21 | @Configuration 22 | public class SleuthSpanSenderConfiguration { 23 | @Autowired 24 | @Qualifier("zipkinKafkaTemplate") 25 | private KafkaTemplate kafkaTemplate; 26 | 27 | @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) 28 | Reporter myReporter() { 29 | return AsyncReporter.create(mySender()); 30 | } 31 | 32 | @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) 33 | MySender mySender() { 34 | return new MySender(); 35 | } 36 | 37 | public class MySender extends Sender { 38 | private BytesMessageEncoder encoder; 39 | 40 | public MySender() { 41 | this.encoder = BytesMessageEncoder.forEncoding(encoding()); 42 | } 43 | 44 | @Override 45 | public Encoding encoding() { 46 | return Encoding.JSON; 47 | } 48 | 49 | @Override 50 | public int messageMaxBytes() { 51 | return Integer.MAX_VALUE; 52 | } 53 | 54 | @Override 55 | public int messageSizeInBytes(List encodedSpans) { 56 | return encoding().listSizeInBytes(encodedSpans); 57 | } 58 | 59 | @Override 60 | public Call sendSpans(List encodedSpans) { 61 | byte[] encodedMessage = encoder.encode(encodedSpans); 62 | kafkaTemplate.send(new ProducerRecord(CommonConstants.ZIPKIN_TOPIC, encodedMessage)); 63 | 64 | return Call.create(null); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /gateway-zuul/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /gateway-zuul/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=gateway-zuul 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 -------------------------------------------------------------------------------- /hystrix-dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /hystrix-dashboard/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /hystrix-dashboard/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/hystrix-dashboard/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /hystrix-dashboard/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /hystrix-dashboard/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Hystrix Dashboard" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/hystrix-dashboard-0.0.1-SNAPSHOT.jar /usr/local/lib/hystrix-dashboard.jar 11 | EXPOSE 9898 12 | ENTRYPOINT ["java","-jar","/usr/local/lib/hystrix-dashboard.jar"] -------------------------------------------------------------------------------- /hystrix-dashboard/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /hystrix-dashboard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-cloud 8 | 1.0.0 9 | ../pom-base/pom-cloud.xml 10 | 11 | 12 | com.nguyenvm 13 | hystrix-dashboard 14 | 0.0.1-SNAPSHOT 15 | jar 16 | hystrix-dashboard 17 | Hystrix dashboard 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-netflix-hystrix-dashboard 23 | 2.2.8.RELEASE 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-maven-plugin 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /hystrix-dashboard/src/main/java/com/nguyenvm/hystrixdashboard/HystrixDashboardApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.hystrixdashboard; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 6 | 7 | @SpringBootApplication 8 | @EnableHystrixDashboard 9 | public class HystrixDashboardApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(HystrixDashboardApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /hystrix-dashboard/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /hystrix-dashboard/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=hystrix-dashboard 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 -------------------------------------------------------------------------------- /order-service/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .DS_Store 4 | *.iml 5 | -------------------------------------------------------------------------------- /order-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Order Service" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/order-service-0.0.1-SNAPSHOT.jar /usr/local/lib/order-service.jar 11 | ENTRYPOINT ["java","-jar","/usr/local/lib/order-service.jar"] -------------------------------------------------------------------------------- /order-service/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) 9 | 10 | -------------------------------------------------------------------------------- /order-service/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /order-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-service-tracking 8 | 1.0.0 9 | ../pom-base/pom-service-tracking.xml 10 | 11 | 12 | com.nguyenvm 13 | order-service 14 | 0.0.1-SNAPSHOT 15 | jar 16 | order-service 17 | Order Service 18 | 19 | 20 | 21 | com.nguyenvm 22 | common 23 | 1.0.0 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-security 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/OrderServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 7 | import org.springframework.context.annotation.ComponentScan; 8 | 9 | @SpringBootApplication 10 | @EnableEurekaClient 11 | @EnableHystrix 12 | @ComponentScan(basePackages = {"com.nguyenvm.common", "com.nguyenvm.orderservice"}) 13 | public class OrderServiceApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(OrderServiceApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/config/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.DeserializationFeature; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.SerializationFeature; 7 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.web.client.RestTemplate; 11 | 12 | @Configuration 13 | public class AppConfig { 14 | // Create a bean for restTemplate to call services 15 | @Bean 16 | @LoadBalanced // Load balance between service instances running at different ports. 17 | public RestTemplate restTemplate() { 18 | return new RestTemplate(); 19 | } 20 | 21 | @Bean 22 | public ObjectMapper objectMapper() { 23 | ObjectMapper mapper = new ObjectMapper(); 24 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 25 | mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 26 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 27 | return mapper; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.config; 2 | 3 | import com.nguyenvm.common.config.properties.KafkaProperties; 4 | import com.nguyenvm.common.model.kafka.JsonSerializer; 5 | import org.apache.kafka.clients.admin.Admin; 6 | import org.apache.kafka.clients.admin.AdminClientConfig; 7 | import org.apache.kafka.clients.producer.ProducerConfig; 8 | import org.apache.kafka.common.serialization.ByteArraySerializer; 9 | import org.apache.kafka.common.serialization.StringSerializer; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.kafka.annotation.EnableKafka; 14 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 15 | import org.springframework.kafka.core.KafkaAdmin; 16 | import org.springframework.kafka.core.KafkaTemplate; 17 | import org.springframework.kafka.core.ProducerFactory; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | @Configuration 23 | @EnableKafka 24 | public class KafkaConfig { 25 | @Autowired 26 | private KafkaProperties kafkaProperties; 27 | 28 | @Bean 29 | public Map adminConfig() { 30 | Map configs = new HashMap<>(); 31 | configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 32 | 33 | return configs; 34 | } 35 | 36 | @Bean 37 | public KafkaAdmin kafkaAdmin() { 38 | return new KafkaAdmin(adminConfig()); 39 | } 40 | 41 | @Bean 42 | public Admin admin() { 43 | Admin admin = Admin.create(adminConfig()); 44 | return admin; 45 | } 46 | 47 | @Bean("producerConfig") 48 | public Map producerConfig() { 49 | Map configs = new HashMap<>(); 50 | configs.put(ProducerConfig.CLIENT_ID_CONFIG, kafkaProperties.getClientId()); 51 | configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 52 | configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); 53 | configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class.getName()); 54 | 55 | return configs; 56 | } 57 | 58 | @Bean("producerWithTransactionConfig") 59 | public Map producerWithTransactionConfig() { 60 | Map configs = new HashMap<>(); 61 | configs.putAll(producerConfig()); 62 | configs.put(ProducerConfig.BUFFER_MEMORY_CONFIG, kafkaProperties.getBufferMemory()); 63 | configs.put(ProducerConfig.RETRIES_CONFIG, kafkaProperties.getRetries()); 64 | configs.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, kafkaProperties.getTransactionId()); 65 | 66 | return configs; 67 | } 68 | 69 | @Bean 70 | public ProducerFactory producerFactory() { 71 | return new DefaultKafkaProducerFactory<>(producerConfig()); 72 | } 73 | 74 | @Bean 75 | public KafkaTemplate kafkaTemplate() { 76 | return new KafkaTemplate<>(producerFactory()); 77 | } 78 | 79 | @Bean("zipkinProducerConfig") 80 | public Map zipkinProducerConfig() { 81 | Map configs = new HashMap<>(); 82 | configs.put(ProducerConfig.CLIENT_ID_CONFIG, kafkaProperties.getZipkinClientId()); 83 | configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 84 | configs.put(ProducerConfig.ACKS_CONFIG, kafkaProperties.getZipkinAcks()); 85 | configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 86 | configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 87 | 88 | return configs; 89 | } 90 | 91 | @Bean("zipkinProducerFactory") 92 | public ProducerFactory zipkinProducerFactory() { 93 | return new DefaultKafkaProducerFactory<>(zipkinProducerConfig()); 94 | } 95 | 96 | @Bean("zipkinKafkaTemplate") 97 | public KafkaTemplate zipkinKafkaTemplate() { 98 | return new KafkaTemplate<>(zipkinProducerFactory()); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/config/SecurityTokenConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.config; 2 | 3 | import com.nguyenvm.common.config.properties.JwtConfig; 4 | import com.nguyenvm.orderservice.config.filter.JwtTokenAuthenticationFilter; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.config.http.SessionCreationPolicy; 12 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 13 | 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.security.interfaces.RSAPrivateKey; 16 | 17 | // Enable security config. This annotation denotes config for spring security. 18 | @Configuration 19 | @EnableWebSecurity 20 | public class SecurityTokenConfig extends WebSecurityConfigurerAdapter { 21 | @Autowired 22 | private JwtConfig jwtConfig; 23 | 24 | @Autowired 25 | @Qualifier("jwtSigningKey") 26 | private RSAPrivateKey privateKey; 27 | 28 | @Override 29 | protected void configure(HttpSecurity http) throws Exception { 30 | http 31 | .csrf().disable() 32 | // make sure we use stateless session; session won't be used to store user's state. 33 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 34 | .and() 35 | // handle an authorized attempts 36 | .exceptionHandling().authenticationEntryPoint((req, res, e) -> res.sendError(HttpServletResponse.SC_UNAUTHORIZED)) 37 | .and() 38 | // Add a filter to validate the tokens with every request 39 | .addFilterAfter(new JwtTokenAuthenticationFilter(jwtConfig, privateKey), UsernamePasswordAuthenticationFilter.class) 40 | // authorization requests config 41 | .authorizeRequests() 42 | // allow all who are accessing "guest" service 43 | .antMatchers("/order/**").access("hasRole('ADMIN')") 44 | .anyRequest().authenticated(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/config/SleuthSpanSenderConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.config; 2 | 3 | import com.nguyenvm.common.util.CommonConstants; 4 | import org.apache.kafka.clients.producer.ProducerRecord; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.cloud.sleuth.autoconfig.zipkin2.ZipkinAutoConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import zipkin2.Call; 12 | import zipkin2.Span; 13 | import zipkin2.codec.Encoding; 14 | import zipkin2.reporter.AsyncReporter; 15 | import zipkin2.reporter.BytesMessageEncoder; 16 | import zipkin2.reporter.Reporter; 17 | import zipkin2.reporter.Sender; 18 | 19 | import java.util.List; 20 | 21 | @Configuration 22 | public class SleuthSpanSenderConfiguration { 23 | @Autowired 24 | @Qualifier("zipkinKafkaTemplate") 25 | private KafkaTemplate kafkaTemplate; 26 | 27 | @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) 28 | Reporter myReporter() { 29 | return AsyncReporter.create(mySender()); 30 | } 31 | 32 | @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) 33 | MySender mySender() { 34 | return new MySender(); 35 | } 36 | 37 | class MySender extends Sender { 38 | private BytesMessageEncoder encoder; 39 | 40 | public MySender() { 41 | this.encoder = BytesMessageEncoder.forEncoding(encoding()); 42 | } 43 | 44 | @Override 45 | public Encoding encoding() { 46 | return Encoding.JSON; 47 | } 48 | 49 | @Override 50 | public int messageMaxBytes() { 51 | return Integer.MAX_VALUE; 52 | } 53 | 54 | @Override 55 | public int messageSizeInBytes(List encodedSpans) { 56 | return encoding().listSizeInBytes(encodedSpans); 57 | } 58 | 59 | @Override 60 | public Call sendSpans(List encodedSpans) { 61 | byte[] encodedMessage = encoder.encode(encodedSpans); 62 | kafkaTemplate.send(new ProducerRecord(CommonConstants.ZIPKIN_TOPIC, encodedMessage)); 63 | 64 | return Call.create(null); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/config/filter/JwtTokenAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.config.filter; 2 | 3 | import com.nguyenvm.common.config.properties.JwtConfig; 4 | import io.jsonwebtoken.Claims; 5 | import io.jsonwebtoken.Jwts; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.web.filter.OncePerRequestFilter; 10 | 11 | import javax.servlet.FilterChain; 12 | import javax.servlet.ServletException; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.io.IOException; 16 | import java.security.interfaces.RSAPrivateKey; 17 | import java.util.List; 18 | import java.util.Objects; 19 | import java.util.stream.Collectors; 20 | 21 | public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { 22 | private final JwtConfig jwtConfig; 23 | private final RSAPrivateKey rsaPrivateKey; 24 | 25 | public JwtTokenAuthenticationFilter(JwtConfig jwtConfig, RSAPrivateKey rsaPrivateKey) { 26 | this.jwtConfig = jwtConfig; 27 | this.rsaPrivateKey = rsaPrivateKey; 28 | } 29 | 30 | @Override 31 | protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { 32 | // 1. get the authentication header. Tokens are supposed to be passed in the authentication header 33 | String header = httpServletRequest.getHeader(jwtConfig.getHeader()); 34 | 35 | // 2. validate the header and check the prefix 36 | if (header == null || !header.startsWith(jwtConfig.getPrefix())) { 37 | filterChain.doFilter(httpServletRequest, httpServletResponse); // If not valid, go to the next filter. 38 | return; 39 | } 40 | 41 | // If there is no token provided and hence the user won't be authenticated. 42 | // It's Ok. Maybe the user accessing a public path or asking for a token. 43 | // All secured paths that needs a token are already defined and secured in config class. 44 | // And If user tried to access without access token, then he won't be authenticated and an exception will be thrown. 45 | // 3. Get the token 46 | String token = header.replace(jwtConfig.getPrefix(), ""); 47 | 48 | try { 49 | // exceptions might be thrown in creating the claims if for example the token is expired 50 | // 4. Validate the token 51 | Claims claims = Jwts.parserBuilder() 52 | .setSigningKey(rsaPrivateKey) 53 | .build() 54 | .parseClaimsJws(token) 55 | .getBody(); 56 | 57 | String username = claims.getSubject(); 58 | if (Objects.nonNull(username)) { 59 | List authorities = (List) claims.get("authorities"); 60 | 61 | // 5. Create auth object 62 | // UsernamePasswordAuthenticationToken: A built-in object, used by spring to represent the current authenticated / being authenticated user. 63 | // It needs a list of authorities, which has type of GrantedAuthority interface, where SimpleGrantedAuthority is an implementation of that interface 64 | UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( 65 | username, 66 | null, 67 | authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()) 68 | ); 69 | 70 | // 6. Authenticate the user 71 | // Now, user is authenticated 72 | SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); 73 | } 74 | 75 | } catch (Exception e) { 76 | // In case of failure. Make sure it's clear; so guarantee user won't be authenticated 77 | SecurityContextHolder.clearContext(); 78 | } 79 | 80 | // go to the next filter in the filter chain 81 | filterChain.doFilter(httpServletRequest, httpServletResponse); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.controller; 2 | 3 | import com.nguyenvm.orderservice.model.dto.OrderDTO; 4 | import com.nguyenvm.orderservice.service.OrderService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | @RequestMapping("/order") 14 | @Slf4j 15 | public class OrderController { 16 | @Autowired 17 | private OrderService orderService; 18 | 19 | @GetMapping 20 | public OrderDTO getOrder(@RequestParam("id") Integer id, 21 | @RequestParam(value = "isFallBack", defaultValue = "false") Boolean isFallBack) { 22 | return orderService.getOrder(id, isFallBack); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/model/dto/OrderDTO.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.model.dto; 2 | 3 | import lombok.*; 4 | 5 | import java.util.List; 6 | 7 | @Getter 8 | @Setter 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class OrderDTO { 13 | private Integer id; 14 | private List products; 15 | private Integer totalAmount; 16 | } 17 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/model/dto/ProductDTO.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.model.dto; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ProductDTO { 11 | private Integer id; 12 | private String name; 13 | private Integer quantity; 14 | private Integer price; 15 | } 16 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/model/dto/mapper/OrderEntityToDTOMapper.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.model.dto.mapper; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.nguyenvm.common.util.ContextUtil; 6 | import com.nguyenvm.orderservice.model.dto.OrderDTO; 7 | import com.nguyenvm.orderservice.model.dto.ProductDTO; 8 | import com.nguyenvm.orderservice.model.entity.OrderEntity; 9 | 10 | import java.util.List; 11 | 12 | public class OrderEntityToDTOMapper { 13 | public static OrderDTO base(OrderEntity orderEntity, List products) { 14 | List productDTOS = ContextUtil.getBean(ObjectMapper.class).convertValue(products, new TypeReference>() {}); 15 | return OrderDTO.builder() 16 | .id(orderEntity.getId()) 17 | .products(productDTOS) 18 | .totalAmount(productDTOS.stream().mapToInt(product -> product.getPrice() * product.getQuantity()).sum()) 19 | .build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/model/entity/OrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.model.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.util.List; 9 | 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class OrderEntity { 15 | private Integer id; 16 | private List products; 17 | private Integer totalAmount; 18 | } 19 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.service; 2 | 3 | import com.nguyenvm.orderservice.model.dto.OrderDTO; 4 | 5 | public interface OrderService { 6 | OrderDTO getOrder(Integer id, Boolean isFallBack); 7 | } 8 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/service/impl/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.service.impl; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import com.nguyenvm.common.util.CommonConstants; 5 | import com.nguyenvm.orderservice.model.dto.OrderDTO; 6 | import com.nguyenvm.orderservice.model.dto.mapper.OrderEntityToDTOMapper; 7 | import com.nguyenvm.orderservice.model.entity.OrderEntity; 8 | import com.nguyenvm.orderservice.service.OrderService; 9 | import com.nguyenvm.orderservice.service.kafka.OrderProducer; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.util.CollectionUtils; 14 | import org.springframework.web.client.RestTemplate; 15 | 16 | import java.util.Collections; 17 | import java.util.List; 18 | 19 | @Service 20 | @Slf4j 21 | public class OrderServiceImpl implements OrderService { 22 | @Autowired 23 | private RestTemplate restTemplate; 24 | 25 | @Autowired 26 | private OrderProducer orderProducer; 27 | 28 | @HystrixCommand(fallbackMethod = "fallBack") 29 | public OrderDTO getOrder(Integer id, Boolean isFallBack) { 30 | log.info("Creating order object ... "); 31 | 32 | // create order object 33 | OrderEntity order = new OrderEntity(); 34 | order.setId(id); 35 | 36 | // get list of available products 37 | // isFallBack = true will make a fallback call 38 | List products = restTemplate.getForObject(CommonConstants.STOCK_SERVICE_URL + isFallBack, List.class); 39 | OrderDTO orderDTO = OrderEntityToDTOMapper.base(order, CollectionUtils.isEmpty(products) ? Collections.EMPTY_LIST : products); 40 | 41 | // send message to order-topic 42 | // orderProducer.produceOrderTopic(orderDTO); 43 | orderProducer.produceOrderTopicUsingKafkaTemplate(orderDTO); 44 | return orderDTO; 45 | } 46 | 47 | // a fallback method to be called if failure happened 48 | public OrderDTO fallBack(Integer id, Boolean isFallBack, Throwable hystrixCommand) { 49 | log.error("Fallback getOrder ... ", hystrixCommand); 50 | 51 | OrderDTO orderDTO = new OrderDTO(id, null, 0); 52 | 53 | // send message to order-topic 54 | orderProducer.produceOrderTopic(orderDTO); 55 | return orderDTO; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/nguyenvm/orderservice/service/kafka/OrderProducer.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice.service.kafka; 2 | 3 | import com.nguyenvm.common.util.CommonConstants; 4 | import com.nguyenvm.orderservice.model.dto.OrderDTO; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.kafka.clients.producer.KafkaProducer; 7 | import org.apache.kafka.clients.producer.ProducerRecord; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | import javax.annotation.Resource; 14 | import java.util.Map; 15 | 16 | @Service 17 | @Slf4j 18 | public class OrderProducer { 19 | private final static String topicTest1 = "nguyenvm-topic-1"; 20 | private final static String topicTest2 = "nguyenvm-topic-2"; 21 | 22 | @Resource 23 | @Qualifier("producerConfig") 24 | private Map producerConfig; 25 | 26 | @Autowired 27 | private KafkaTemplate kafkaTemplate; 28 | 29 | @Resource 30 | @Qualifier("producerWithTransactionConfig") 31 | private Map producerWithTransactionConfig; 32 | 33 | public void produceOrderTopic(OrderDTO orderDTO) { 34 | try (KafkaProducer producer = new KafkaProducer(producerConfig)) { 35 | producer.send(new ProducerRecord(CommonConstants.ORDER_TOPIC, orderDTO)); 36 | } 37 | } 38 | 39 | // transaction kafka with producer message 40 | public void produceMessageWithTransaction(OrderDTO orderDTO) { 41 | KafkaProducer producer = new KafkaProducer(producerWithTransactionConfig); 42 | producer.initTransactions(); 43 | 44 | try { 45 | producer.beginTransaction(); 46 | 47 | producer.send(new ProducerRecord(topicTest1, orderDTO)); 48 | producer.send(new ProducerRecord(topicTest2, orderDTO)); 49 | 50 | producer.commitTransaction(); 51 | } catch (Exception e) { 52 | log.error(e.getMessage(), e); 53 | 54 | producer.abortTransaction(); 55 | producer.close(); 56 | throw new RuntimeException(e); 57 | } 58 | 59 | producer.close(); 60 | } 61 | 62 | public void produceOrderTopicUsingKafkaTemplate(OrderDTO orderDTO) { 63 | kafkaTemplate.send(new ProducerRecord(CommonConstants.ORDER_TOPIC, orderDTO)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /order-service/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /order-service/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=order-service 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 -------------------------------------------------------------------------------- /order-service/src/test/java/com/nguyenvm/orderservice/EurekaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.orderservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class EurekaApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /pom-base/pom-build.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.5.0 9 | 10 | 11 | 12 | com.nguyenvm 13 | pom-build 14 | 1.0.0 15 | Pom build 16 | Parent pom build project 17 | pom 18 | 19 | 20 | 1.8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 2.5.0 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | org.projectlombok 38 | lombok 39 | 1.18.20 40 | 41 | 42 | 43 | org.apache.commons 44 | commons-lang3 45 | 3.12.0 46 | 47 | 48 | 49 | javax.validation 50 | validation-api 51 | 2.0.1.Final 52 | 53 | 54 | 55 | io.jsonwebtoken 56 | jjwt-api 57 | 0.11.2 58 | 59 | 60 | 61 | io.jsonwebtoken 62 | jjwt-impl 63 | 0.11.2 64 | 65 | 66 | 67 | io.jsonwebtoken 68 | jjwt-jackson 69 | 0.11.2 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /pom-base/pom-cloud.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-build 8 | 1.0.0 9 | pom-build.xml 10 | 11 | 12 | com.nguyenvm 13 | pom-cloud 14 | 1.0.0 15 | Pom cloud 16 | Parent pom cloud project 17 | pom 18 | 19 | 20 | 2020.0.3 21 | 22 | 23 | 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-dependencies 28 | ${spring-cloud.version} 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-config 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-bootstrap 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /pom-base/pom-service-tracking.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-service 8 | 1.0.0 9 | pom-service.xml 10 | 11 | 12 | com.nguyenvm 13 | pom-service-tracking 14 | 1.0.0 15 | Pom service 16 | Parent pom service tracking project 17 | pom 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-actuator 23 | 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-hystrix 28 | 2.2.8.RELEASE 29 | 30 | 31 | org.springframework.cloud 32 | spring-cloud-starter-netflix-archaius 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-netflix-archaius 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-netflix-hystrix-stream 44 | 2.2.8.RELEASE 45 | 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-starter-stream-kafka 50 | 51 | 52 | 53 | org.springframework.cloud 54 | spring-cloud-starter-sleuth 55 | 56 | 57 | 58 | org.springframework.cloud 59 | spring-cloud-starter-zipkin 60 | 2.2.8.RELEASE 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /pom-base/pom-service.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-cloud 8 | 1.0.0 9 | pom-cloud.xml 10 | 11 | 12 | com.nguyenvm 13 | pom-service 14 | 1.0.0 15 | Pom service 16 | Parent pom service project 17 | pom 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-netflix-eureka-client 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | micro-service 8 | 0.0.1-SNAPSHOT 9 | Microservices Aggregator 10 | Spring Microservices 11 | pom 12 | 13 | 14 | 15 | 0001 16 | Louis Van 17 | vanminhnguyenbmt@gmail.com 18 | 19 | 20 | Software Engineer 21 | 22 | 23 | 24 | 25 | 26 | 27 | common 28 | config-server 29 | eureka-server 30 | gateway-zuul 31 | auth-service 32 | stock-service 33 | order-service 34 | hystrix-dashboard 35 | turbine-stream 36 | 37 | 38 | -------------------------------------------------------------------------------- /scripts/common-module.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | mvn clean install -f pom-base/pom-build.xml 3 | mvn clean install -f pom-base/pom-cloud.xml 4 | mvn clean install -f pom-base/pom-service.xml 5 | mvn clean install -f pom-base/pom-service-tracking.xml 6 | cd ./common 7 | mvn -U clean install -DskipTests=true -------------------------------------------------------------------------------- /scripts/create-topic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | awk -F':' '{ system("docker exec -t zookeeper /bin/kafka-topics --bootstrap-server kafka-1:9092,kafka-2:9092,kafka-3:9092 --topic=" $1 " --create --partitions=" $2 " --replication-factor=" $3) }' ./scripts/kafka-topics.txt -------------------------------------------------------------------------------- /scripts/docker-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | command=$2 4 | if [ $2 == up ] 5 | then 6 | command="$command -d" 7 | fi 8 | 9 | if [ $2 == reset ] 10 | then 11 | command="up --build --remove-orphans --force-recreate" 12 | fi 13 | 14 | docker-compose -f $1/docker-compose.yml $command -------------------------------------------------------------------------------- /scripts/kafka-topics.txt: -------------------------------------------------------------------------------- 1 | hystrixStreamOutput:3:3 2 | zipkin:3:3 3 | order-topic:3:3 -------------------------------------------------------------------------------- /scripts/maven-setup.sh: -------------------------------------------------------------------------------- 1 | cd $1 2 | mvn -U clean install -DskipTests=true -------------------------------------------------------------------------------- /stock-service/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .DS_Store 4 | *.iml 5 | -------------------------------------------------------------------------------- /stock-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Stock Service" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/stock-service-0.0.1-SNAPSHOT.jar /usr/local/lib/stock-service.jar 11 | ENTRYPOINT ["java","-jar","/usr/local/lib/stock-service.jar"] -------------------------------------------------------------------------------- /stock-service/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) 9 | 10 | -------------------------------------------------------------------------------- /stock-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-service-tracking 8 | 1.0.0 9 | ../pom-base/pom-service-tracking.xml 10 | 11 | 12 | com.nguyenvm 13 | stock-service 14 | 0.0.1-SNAPSHOT 15 | jar 16 | stock-service 17 | Stock Service 18 | 19 | 20 | 21 | com.nguyenvm 22 | common 23 | 1.0.0 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-maven-plugin 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/StockServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 7 | import org.springframework.context.annotation.ComponentScan; 8 | 9 | @SpringBootApplication 10 | @EnableEurekaClient 11 | @EnableHystrix 12 | @ComponentScan(basePackages = {"com.nguyenvm.common", "com.nguyenvm.stockservice"}) 13 | public class StockServiceApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(StockServiceApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/config/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.DeserializationFeature; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.SerializationFeature; 7 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.web.client.RestTemplate; 11 | 12 | @Configuration 13 | public class AppConfig { 14 | // Create a bean for restTemplate to call services 15 | @Bean 16 | @LoadBalanced // Load balance between service instances running at different ports. 17 | public RestTemplate restTemplate() { 18 | return new RestTemplate(); 19 | } 20 | 21 | @Bean 22 | public ObjectMapper objectMapper() { 23 | ObjectMapper mapper = new ObjectMapper(); 24 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 25 | mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 26 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 27 | return mapper; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.config; 2 | 3 | import com.nguyenvm.common.config.properties.KafkaProperties; 4 | import com.nguyenvm.common.model.kafka.JsonDeserializer; 5 | import com.nguyenvm.stockservice.model.dto.OrderDTO; 6 | import org.apache.kafka.clients.admin.Admin; 7 | import org.apache.kafka.clients.admin.AdminClientConfig; 8 | import org.apache.kafka.clients.consumer.ConsumerConfig; 9 | import org.apache.kafka.clients.producer.ProducerConfig; 10 | import org.apache.kafka.common.serialization.ByteArraySerializer; 11 | import org.apache.kafka.common.serialization.StringDeserializer; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 16 | import org.springframework.kafka.core.*; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | @Configuration 22 | public class KafkaConfig { 23 | @Autowired 24 | private KafkaProperties kafkaProperties; 25 | 26 | @Bean 27 | public Map adminConfig() { 28 | Map configs = new HashMap<>(); 29 | configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 30 | 31 | return configs; 32 | } 33 | 34 | @Bean 35 | public KafkaAdmin kafkaAdmin() { 36 | return new KafkaAdmin(adminConfig()); 37 | } 38 | 39 | @Bean 40 | public Admin admin() { 41 | Admin admin = Admin.create(adminConfig()); 42 | return admin; 43 | } 44 | 45 | @Bean("consumerConfig") 46 | public Map consumerConfig() { 47 | Map configs = new HashMap<>(); 48 | configs.put(ConsumerConfig.CLIENT_ID_CONFIG, kafkaProperties.getClientId()); 49 | configs.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 50 | configs.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); 51 | configs.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class.getName()); 52 | configs.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaProperties.getGroupId()); 53 | configs.put(ConsumerConfig.ALLOW_AUTO_CREATE_TOPICS_CONFIG, kafkaProperties.getAutoCreateTopic()); 54 | configs.put(JsonDeserializer.VALUE_CLASS_NAME_CONFIG, OrderDTO.class); 55 | 56 | return configs; 57 | } 58 | 59 | @Bean 60 | public ConsumerFactory consumerFactory() { 61 | return new DefaultKafkaConsumerFactory<>(consumerConfig()); 62 | } 63 | 64 | @Bean 65 | public ConcurrentKafkaListenerContainerFactory customKafkaListenerContainerFactory() { 66 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 67 | factory.setConsumerFactory(consumerFactory()); 68 | 69 | return factory; 70 | } 71 | 72 | @Bean("zipkinProducerConfig") 73 | public Map zipkinProducerConfig() { 74 | Map configs = new HashMap<>(); 75 | configs.put(ProducerConfig.CLIENT_ID_CONFIG, kafkaProperties.getZipkinClientId()); 76 | configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); 77 | configs.put(ProducerConfig.ACKS_CONFIG, kafkaProperties.getZipkinAcks()); 78 | configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 79 | configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); 80 | 81 | return configs; 82 | } 83 | 84 | @Bean("zipkinProducerFactory") 85 | public ProducerFactory zipkinProducerFactory() { 86 | return new DefaultKafkaProducerFactory<>(zipkinProducerConfig()); 87 | } 88 | 89 | @Bean("zipkinKafkaTemplate") 90 | public KafkaTemplate zipkinKafkaTemplate() { 91 | return new KafkaTemplate<>(zipkinProducerFactory()); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/config/SleuthSpanSenderConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.config; 2 | 3 | import com.nguyenvm.common.util.CommonConstants; 4 | import org.apache.kafka.clients.producer.ProducerRecord; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.cloud.sleuth.autoconfig.zipkin2.ZipkinAutoConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import zipkin2.Call; 12 | import zipkin2.Span; 13 | import zipkin2.codec.Encoding; 14 | import zipkin2.reporter.AsyncReporter; 15 | import zipkin2.reporter.BytesMessageEncoder; 16 | import zipkin2.reporter.Reporter; 17 | import zipkin2.reporter.Sender; 18 | 19 | import java.util.List; 20 | 21 | @Configuration 22 | public class SleuthSpanSenderConfiguration { 23 | @Autowired 24 | @Qualifier("zipkinKafkaTemplate") 25 | private KafkaTemplate kafkaTemplate; 26 | 27 | @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) 28 | Reporter myReporter() { 29 | return AsyncReporter.create(mySender()); 30 | } 31 | 32 | @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) 33 | MySender mySender() { 34 | return new MySender(); 35 | } 36 | 37 | class MySender extends Sender { 38 | private BytesMessageEncoder encoder; 39 | 40 | public MySender() { 41 | this.encoder = BytesMessageEncoder.forEncoding(encoding()); 42 | } 43 | 44 | @Override 45 | public Encoding encoding() { 46 | return Encoding.JSON; 47 | } 48 | 49 | @Override 50 | public int messageMaxBytes() { 51 | return Integer.MAX_VALUE; 52 | } 53 | 54 | @Override 55 | public int messageSizeInBytes(List encodedSpans) { 56 | return encoding().listSizeInBytes(encodedSpans); 57 | } 58 | 59 | @Override 60 | public Call sendSpans(List encodedSpans) { 61 | byte[] encodedMessage = encoder.encode(encodedSpans); 62 | kafkaTemplate.send(new ProducerRecord(CommonConstants.ZIPKIN_TOPIC, encodedMessage)); 63 | 64 | return Call.create(null); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/controller/StockController.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.controller; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import com.nguyenvm.stockservice.model.dto.ProductDTO; 5 | import com.nguyenvm.stockservice.service.StockService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | @RestController 15 | @RequestMapping("/stock/product") 16 | public class StockController { 17 | @Autowired 18 | private StockService stockService; 19 | 20 | @HystrixCommand 21 | @GetMapping("/getAll") 22 | public List getListProduct(@RequestParam(name = "isFallBack", defaultValue = "false") boolean isFallBack) throws Exception { 23 | return stockService.getListProduct(isFallBack); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/model/dto/OrderDTO.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.model.dto; 2 | 3 | import lombok.*; 4 | 5 | import java.util.List; 6 | 7 | @Getter 8 | @Setter 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class OrderDTO { 13 | private Integer id; 14 | private List products; 15 | private Integer totalAmount; 16 | } 17 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/model/dto/ProductDTO.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.model.dto; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ProductDTO { 11 | private Integer id; 12 | private String name; 13 | private Integer quantity; 14 | private Integer price; 15 | } 16 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/model/dto/mapper/ProductEntityToDTOMapper.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.model.dto.mapper; 2 | 3 | import com.nguyenvm.stockservice.model.dto.ProductDTO; 4 | import com.nguyenvm.stockservice.model.entity.ProductEntity; 5 | 6 | public class ProductEntityToDTOMapper { 7 | public static ProductDTO base(ProductEntity productEntity) { 8 | return ProductDTO.builder() 9 | .id(productEntity.getId()) 10 | .name(productEntity.getName()) 11 | .quantity(productEntity.getQuantity()) 12 | .price(productEntity.getPrice()) 13 | .build(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/model/entity/ProductEntity.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.model.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class ProductEntity { 13 | private Integer id; 14 | private String name; 15 | private Integer quantity; 16 | private Integer price; 17 | } 18 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/service/StockService.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.service; 2 | 3 | import com.nguyenvm.stockservice.model.dto.ProductDTO; 4 | 5 | import java.util.List; 6 | 7 | public interface StockService { 8 | List getListProduct(Boolean isFallBack) throws Exception; 9 | } 10 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/service/impl/StockServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.service.impl; 2 | 3 | import com.nguyenvm.stockservice.model.dto.ProductDTO; 4 | import com.nguyenvm.stockservice.model.dto.mapper.ProductEntityToDTOMapper; 5 | import com.nguyenvm.stockservice.model.entity.ProductEntity; 6 | import com.nguyenvm.stockservice.service.StockService; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | @Service 14 | public class StockServiceImpl implements StockService { 15 | 16 | private List mockList() { 17 | return Arrays.asList( 18 | new ProductEntity(1, "Coffee 1", 3, 30000), 19 | new ProductEntity(2, "Coffee 2", 5, 50000), 20 | new ProductEntity(3, "Coffee 3", 7, 70000) 21 | ); 22 | } 23 | 24 | public List getListProduct(Boolean isFallBack) throws Exception { 25 | // testing fallback for order-service 26 | if (isFallBack) { 27 | throw new Exception("Products can't be fetched"); 28 | } 29 | 30 | return mockList() 31 | .stream() 32 | .map(ProductEntityToDTOMapper::base) 33 | .collect(Collectors.toList()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /stock-service/src/main/java/com/nguyenvm/stockservice/service/kafka/StockConsumer.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice.service.kafka; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.nguyenvm.common.util.CommonConstants; 6 | import com.nguyenvm.stockservice.model.dto.OrderDTO; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.kafka.annotation.KafkaListener; 10 | import org.springframework.stereotype.Service; 11 | 12 | @Service 13 | @Slf4j 14 | public class StockConsumer { 15 | @Autowired 16 | private ObjectMapper objectMapper; 17 | 18 | @KafkaListener(topics = {CommonConstants.ORDER_TOPIC}, containerFactory = "customKafkaListenerContainerFactory") 19 | public void consumerOrder(OrderDTO orderDTO) { 20 | try { 21 | log.info("[CONSUMER] Consumer order-topic: {}", objectMapper.writeValueAsString(orderDTO)); 22 | } catch (JsonProcessingException e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /stock-service/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /stock-service/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=stock-service 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 -------------------------------------------------------------------------------- /stock-service/src/test/java/com/nguyenvm/imageservice/EurekaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.stockservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class EurekaApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /turbine-stream/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /turbine-stream/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /turbine-stream/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanminhnguyenbmt/spring-microservices/c57208447ae3caef2f3d4c02e86ec5cb619e22ba/turbine-stream/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /turbine-stream/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /turbine-stream/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-buster 2 | 3 | # Set image information, but could be set to different location from command line 4 | ARG IMAGE_VERSION="0.0.1-SNAPSHOT" 5 | ARG IMAGE_NAME="Turbine Stream" 6 | ARG MAINTAINER="Louis Van " 7 | 8 | LABEL version=${IMAGE_VERSION} name=${IMAGE_NAME} maintainer=${MAINTAINER} 9 | 10 | COPY target/turbine-stream-0.0.1-SNAPSHOT.jar /usr/local/lib/turbine-stream.jar 11 | EXPOSE 8989 12 | ENTRYPOINT ["java","-jar","/usr/local/lib/turbine-stream.jar"] -------------------------------------------------------------------------------- /turbine-stream/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.nguyenvm 7 | pom-cloud 8 | 1.0.0 9 | ../pom-base/pom-cloud.xml 10 | 11 | 12 | com.nguyenvm 13 | turbine-stream 14 | 0.0.1-SNAPSHOT 15 | jar 16 | turbine-stream 17 | Turbine Stream 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-netflix-turbine-stream 23 | 2.2.8.RELEASE 24 | 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-stream-binder-kafka 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /turbine-stream/src/main/java/com/nguyenvm/turbinestraem/TurbineStreamApplication.java: -------------------------------------------------------------------------------- 1 | package com.nguyenvm.turbinestraem; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.turbine.stream.EnableTurbineStream; 6 | 7 | @SpringBootApplication 8 | @EnableTurbineStream 9 | public class TurbineStreamApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(TurbineStreamApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /turbine-stream/src/main/resources/bootstrap-docker.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://config-server:8888 -------------------------------------------------------------------------------- /turbine-stream/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.name=turbine-stream 2 | spring.cloud.config.uri=http://localhost:8888 3 | spring.cloud.config.username=${CONFIG_SERVER_USERNAME} 4 | spring.cloud.config.password=${CONFIG_SERVER_PASSWORD} 5 | spring.cloud.config.fail-fast=true 6 | spring.cloud.config.retry.multiplier=1.3 7 | spring.cloud.config.retry.initial-interval=3000 8 | spring.cloud.config.retry.max-interval=10000 9 | spring.cloud.config.retry.max-attempts=20 --------------------------------------------------------------------------------