Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ivangfr/springboot-rsocket-webflux-aop
The goal of this project is to play with RSocket protocol. For it, we will implement three Spring Boot Java applications, movie-server, movie-client-shell and movie-client-ui. As storage, it's used the reactive NoSQL database MongoDB. All the streaming of movie events and the logging are handling by AOP (Aspect Oriented Programming).
https://github.com/ivangfr/springboot-rsocket-webflux-aop
aop docker java jib mongodb native reactive rsocket semantic-ui spring-boot spring-data-mongodb-reactive spring-native spring-shell thymeleaf webflux websocket
Last synced: 3 months ago
JSON representation
The goal of this project is to play with RSocket protocol. For it, we will implement three Spring Boot Java applications, movie-server, movie-client-shell and movie-client-ui. As storage, it's used the reactive NoSQL database MongoDB. All the streaming of movie events and the logging are handling by AOP (Aspect Oriented Programming).
- Host: GitHub
- URL: https://github.com/ivangfr/springboot-rsocket-webflux-aop
- Owner: ivangfr
- Created: 2020-07-18T08:47:18.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-04-13T21:53:16.000Z (10 months ago)
- Last Synced: 2024-05-07T18:23:17.806Z (9 months ago)
- Topics: aop, docker, java, jib, mongodb, native, reactive, rsocket, semantic-ui, spring-boot, spring-data-mongodb-reactive, spring-native, spring-shell, thymeleaf, webflux, websocket
- Language: Java
- Homepage:
- Size: 14.7 MB
- Stars: 22
- Watchers: 2
- Forks: 8
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# springboot-rsocket-webflux-aop
The goal of this project is to play with [`RSocket`](https://rsocket.io/) protocol. For it, we will implement three [`Spring Boot`](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) Java applications: `movie-server`, `movie-client-shell` and `movie-client-ui`. As storage, it's used the reactive NoSQL database [`MongoDB`](https://www.mongodb.com/). All the streaming of movie events and the logging are handling by AOP (Aspect Oriented Programming).
## Proof-of-Concepts & Articles
On [ivangfr.github.io](https://ivangfr.github.io), I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for.
## Project Diagram
![project-diagram](documentation/project-diagram.jpeg)
## Applications
- ### movie-server
`Spring Boot` Java Web application that exposes REST API endpoints or RSocket routes to manage `movies`. Movies data are stored in reactive `MongoDB`.
`movie-server` has the following profiles:
- `default`
- start REST API on port `8080` and uses `HTTP`
- `rsocket-tcp`
- start REST API on port `8080` and uses `HTTP`
- start RSocket on port `7000` and uses `TCP`
- `rsocket-websocket`
- start REST API on port `8080` and uses `HTTP`
- start RSocket with mapping-path `/rsocket` and uses `WebSocket`- ### movie-client-shell
`Spring Boot` Shell Java application that has a couple of commands to interact with `movie-server`.
The picture below show those commands
![movie-client-shell](documentation/movie-client-shell.jpeg)It has the following profiles:
- `default`
- start shell with enabled commands to call `movie-server` REST API endpoints using `HTTP`
- `rsocket-tcp`
- start shell with enabled commands to call `movie-server` REST API endpoints using `HTTP`
- start shell with enabled commands to call `movie-server` RSocket routes using `TCP`
- `rsocket-websocket`
- start shell with enabled commands to call `movie-server` REST API endpoints using `HTTP`
- start shell with enabled commands to call `movie-server` RSocket routes using `WebSocket`- ### movie-client-ui
`Spring Boot` Java Web application that uses [`Thymeleaf`](https://www.thymeleaf.org/) and `Websocket` to show at real-time all the events generated when movies are added, deleted, liked and disliked.
![movie-client-ui](documentation/movie-client-ui.jpeg)
`movie-client-ui` has the following profiles:
- `default`
- start REST API on port `8081` and uses `HTTP`
- does not connect to `movie-server` through `RSocket`; does not receive movie events;
- `rsocket-tcp`
- start REST API on port `8080` and uses `HTTP`
- connects to `movie-server` through `RSocket`using `TCP`; receives movie events;
- `rsocket-websocket`
- start REST API on port `8080` and uses `HTTP`
- connects to `movie-server` through `RSocket`using `WebSocket`; receives movie events;## Demo
The GIF below shows a user running some commands in `movie-client-shell`, terminal on the right. In the right-top terminal is running `movie-server` and in the right-bottom, `movie-client-ui`. On the background, there's a browser where movie events are displayed.
![demo](documentation/demo.gif)
## Prerequisites
- [`Java 17+`](https://www.oracle.com/java/technologies/downloads/#java17)
- [`Docker`](https://www.docker.com/)## Start Environment
Open a terminal and inside `springboot-rsocket-webflux-aop` root folder run
```
docker compose up -d
```## Running applications with Maven
- **movie-server**
Open a new terminal and, inside `springboot-rsocket-webflux-aop` root folder, run one of the following profile's command
| Profile | Command |
|-------------------|---------------------------------------------------------------------------------------------------|
| rsocket-tcp | ./mvnw clean spring-boot:run --projects movie-server -Dspring-boot.run.profiles=rsocket-tcp |
| rsocket-websocket | ./mvnw clean spring-boot:run --projects movie-server -Dspring-boot.run.profiles=rsocket-websocket |
| default | ./mvnw clean spring-boot:run --projects movie-server |
- **movie-client-shell**Open a new terminal and, inside `springboot-rsocket-webflux-aop` root folder, run the following command to build the executable jar file
```
./mvnw clean package --projects movie-client-shell -DskipTests
```To start `movie-client-shell`, run the profile's command you picked to run `movie-server`
| Profile | Commands |
|-------------------|-------------------------------------------------------------------------------------------------------------|
| rsocket-tcp | export SPRING_PROFILES_ACTIVE=rsocket-tcp && ./movie-client-shell/target/movie-client-shell-1.0.0.jar |
| rsocket-websocket | export SPRING_PROFILES_ACTIVE=rsocket-websocket && ./movie-client-shell/target/movie-client-shell-1.0.0.jar |
| default | export SPRING_PROFILES_ACTIVE=default && ./movie-client-shell/target/movie-client-shell-1.0.0.jar |- **movie-client-ui**
Open a new terminal and, inside `springboot-rsocket-webflux-aop` root folder, run the profile's command you picked to run `movie-server`
| Profile | Command |
|-------------------|------------------------------------------------------------------------------------------------------|
| rsocket-tcp | ./mvnw clean spring-boot:run --projects movie-client-ui -Dspring-boot.run.profiles=rsocket-tcp |
| rsocket-websocket | ./mvnw clean spring-boot:run --projects movie-client-ui -Dspring-boot.run.profiles=rsocket-websocket |
| default | ./mvnw clean spring-boot:run --projects movie-client-ui |## Running applications as Docker containers
- ### Build Docker images
- In a terminal, make sure you are in `springboot-rsocket-webflux-aop` root folder
- Run the following script to build the Docker images
- JVM
```
./docker-build.sh
```
- Native (it's not working yet, see [Issues](#issues))
```
./docker-build.sh native
```- ### Environment variables
- **movie-server**
| Environment Variable | Description |
|----------------------|-------------------------------------------------------------------|
| `MONGODB_HOST` | Specify host of the `Mongo` database to use (default `localhost`) |
| `MONGODB_PORT` | Specify port of the `Mongo` database to use (default `27017`) |- **movie-client-shell**
| Environment Variable | Description |
|----------------------|-----------------------------------------------------------------|
| `MOVIE_SERVER_HOST` | Specify host of the `movie-server` to use (default `localhost`) |- **movie-client-ui**
| Environment Variable | Description |
|----------------------|-----------------------------------------------------------------|
| `MOVIE_SERVER_HOST` | Specify host of the `movie-server` to use (default `localhost`) |- ### Start Docker containers
- In a terminal, make sure you are inside `springboot-rsocket-webflux-aop` root folder
- Run following command
- rsocket-tcp
```
./start-server-and-ui.sh rsocket-tcp && ./start-shell.sh rsocket-tcp
```
- rsocket-websocket
```
./start-server-and-ui.sh rsocket-websocket && ./start-shell.sh rsocket-websocket
```
- default
```
./start-server-and-ui.sh && ./start-shell.sh
```## Application's URL
| Application | Type | Transport | URL |
|-----------------|----------|-----------|-----------------------------|
| movie-server | RSocket | TCP | tcp://localhost:7000 |
| movie-server | RSocket | WebSocket | ws://localhost:8080/rsocket |
| movie-server | REST | HTTP | http://localhost:8080 |
| movie-client-ui | Website | HTTP | http://localhost:8081 |> **Note**: you can see the clients connected to `movie-server` by calling the `info` actuator endpoint
> ```
> curl -i localhost:8080/actuator/info
> ```## Playing Around with movie-client-shell commands
> **Note**: to run the commands below, you must start `movie-server` and `movie-client-shell` with `rsocket-tcp` or `rsocket-websocket` profiles
- Open a browser and access `movie-client-ui` at http://localhost:8081
- Go to `movie-client-shell` terminal
- Add a movie using RSocket (`Request-Response`)
```
add-movie-rsocket --imdb aaa --title "RSocketland"
```
It should return
```
{"imdb":"aaa","title":"RSocketland","lastModifiedDate":"2020-07-20T12:43:39.857248","likes":0,"dislikes":0}
```
A `+` action should be displayed in `movie-client-ui`
- Add a movie using REST
```
add-movie-rest --imdb bbb --title "I, REST"
```
It should return
```
{"imdb":"bbb","title":"I, REST","lastModifiedDate":"2020-07-20T12:44:13.266657","likes":0,"dislikes":0}
```
A `+` action should be displayed in `movie-client-ui`
- Send a like to `RSocketland` movie using RSocket (`Fire-And-Forget`)
```
like-movie-rsocket --imdb aaa
```
It should return
```
Like submitted
```
A `thumbs-up` action should be displayed in `movie-client-ui`- Get all movies using RSocket (`Request-Stream`)
```
get-movies-rsocket
```
It should return
```
{"imdb":"aaa","title":"RSocketland","lastModifiedDate":"2020-07-20T12:56:34.565","likes":1,"dislikes":0}
{"imdb":"bbb","title":"I, REST","lastModifiedDate":"2020-07-20T12:56:26.846","likes":0,"dislikes":0}
```
- Select movies using RSocket (`Channel`)
```
select-movies-rsocket --imdbs aaa,bbb
```
It should return
```
| IMBD: aaa | TITLE: RSocketland | LIKES: 1 | DISLIKES: 0 |
| IMBD: bbb | TITLE: I, REST | LIKES: 0 | DISLIKES: 0 |
```
- Delete movie `RSocketland` using RSocket (`Request-Response`) and movie `I, REST` using REST
```
delete-movie-rsocket --imdb aaa
delete-movie-rest --imdb bbb
```
It should return, as response, the IMDB of the movies
A `-` actions should be displayed in `movie-client-ui`
- **Simulation**There are two scripts that contain some commands to add, retrieve, like, dislikes and delete movies. One uses REST and another RSocket to communicate with `movie-server`. At the end of the script execution, it's shown the `Execution Time` in `milliseconds`.
- If you are running the applications with Maven
- REST
```
script ../src/main/resources/simulation-rest.txt
```
- RSocket
```
script ../src/main/resources/simulation-rsocket.txt
```- If you are running the applications as Docker containers
- REST
```
script /app/resources/simulation-rest.txt
```
- RSocket
```
script /app/resources/simulation-rsocket.txt
```## Useful Commands
- **MongoDB**
Find all movies
```
docker exec -it mongodb mongosh moviedb
db.movies.find()
```
> Type `exit` to get out of `MongoDB` shell## Shutdown
- To stop `movie-client-shell`, go to the terminal where it is running and type `exit`
- To stop `movie-server` and `movie-client-ui`
- If you start them with Maven, go to the terminals where they are running and press `Ctrl+C`
- If you start them as Docker containers, go to a terminal and, inside `springboot-rsocket-webflux-aop` root folder, run the following command
```
./stop-server-and-ui.sh
```
- To stop and remove docker compose `mongodb` container, network and volumes, go to a terminal and, inside `springboot-rsocket-webflux-aop` root folder, run the command below
```
docker compose down -v
```## Cleanup
To remove the Docker images created by this project, go to a terminal and, inside `springboot-rsocket-webflux-aop` root folder, run the script below
```
./remove-docker-images.sh
```## References
- https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#rsocket
- https://spring.io/blog/2020/05/12/getting-started-with-rsocket-servers-calling-clients
- https://grapeup.com/blog/reactive-service-to-service-communication-with-rsocket-introduction/## Issues
#### movie-server
Docker native image builds and starts up successfully. However, it doesn't accept connections even using JVM version of `movie-client-ui` and `movie-client-shell`.
#### movie-client-ui
After building and starting the Docker native image successfully, the app looks fine (i.e., we can open the page, websocket is connected, etc). However, it is not connecting to `movie-server`.
#### movie-client-shell
After building the Docker native image and running it successfully, we have some problems such as
- there is the following WARN
```
Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)
```
- the autocomplete is not working;
- when tried to run one of script files (`simulation-rest.txt` or `simulation-rsocket.txt`) there is the following exception
```
movie-client-shell> script /app/resources/simulation-rest.txt
java.lang.NullPointerException
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
movie-client-shell> stacktrace
java.lang.NullPointerException
at [email protected]/java.io.FileInputStream.(FileInputStream.java:149)
at [email protected]/java.io.FileReader.(FileReader.java:75)
at org.springframework.shell.standard.commands.Script.script(Script.java:60)
at [email protected]/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.shell.command.invocation.InvocableShellMethod.doInvoke(InvocableShellMethod.java:306)
at org.springframework.shell.command.invocation.InvocableShellMethod.invoke(InvocableShellMethod.java:232)
at org.springframework.shell.command.CommandExecution$DefaultCommandExecution.evaluate(CommandExecution.java:227)
at org.springframework.shell.Shell.evaluate(Shell.java:248)
at org.springframework.shell.Shell.run(Shell.java:159)
at org.springframework.shell.jline.InteractiveShellRunner.run(InteractiveShellRunner.java:73)
at org.springframework.shell.DefaultShellApplicationRunner.run(DefaultShellApplicationRunner.java:65)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:319)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
at com.ivanfranchin.movieclientshell.MovieClientShellApplication.main(MovieClientShellApplication.java:10)
```