chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# Calculator HTTP Streaming Demo
|
||||
|
||||
This project demonstrates HTTP streaming using Server-Sent Events (SSE) with Spring Boot WebFlux. It consists of two applications:
|
||||
|
||||
- **Calculator Server**: A reactive web service that performs calculations and streams results using SSE
|
||||
- **Calculator Client**: A client application that consumes the streaming endpoint
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Java 17 or higher
|
||||
- Maven 3.6 or higher
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
java/
|
||||
├── calculator-server/ # Spring Boot server with SSE endpoint
|
||||
│ ├── src/main/java/com/example/calculatorserver/
|
||||
│ │ ├── CalculatorServerApplication.java
|
||||
│ │ └── CalculatorController.java
|
||||
│ └── pom.xml
|
||||
├── calculator-client/ # Spring Boot client application
|
||||
│ ├── src/main/java/com/example/calculatorclient/
|
||||
│ │ └── CalculatorClientApplication.java
|
||||
│ └── pom.xml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. The **Calculator Server** exposes a `/calculate` endpoint that:
|
||||
- Accepts query parameters: `a` (number), `b` (number), `op` (operation)
|
||||
- Supported operations: `add`, `sub`, `mul`, `div`
|
||||
- Returns Server-Sent Events with calculation progress and result
|
||||
|
||||
2. The **Calculator Client** connects to the server and:
|
||||
- Makes a request to calculate `7 * 5`
|
||||
- Consumes the streaming response
|
||||
- Prints each event to the console
|
||||
|
||||
## Running the Applications
|
||||
|
||||
### Option 1: Using Maven (Recommended)
|
||||
|
||||
#### 1. Start the Calculator Server
|
||||
|
||||
Open a terminal and navigate to the server directory:
|
||||
|
||||
```bash
|
||||
cd calculator-server
|
||||
mvn clean package
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
The server will start on `http://localhost:8080`
|
||||
|
||||
You should see output like:
|
||||
```
|
||||
Started CalculatorServerApplication in X.XXX seconds
|
||||
Netty started on port 8080 (http)
|
||||
```
|
||||
|
||||
#### 2. Run the Calculator Client
|
||||
|
||||
Open a **new terminal** and navigate to the client directory:
|
||||
|
||||
```bash
|
||||
cd calculator-client
|
||||
mvn clean package
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
The client will connect to the server, perform the calculation, and display the streaming results.
|
||||
|
||||
### Option 2: Using Java directly
|
||||
|
||||
#### 1. Compile and run the server:
|
||||
|
||||
```bash
|
||||
cd calculator-server
|
||||
mvn clean package
|
||||
java -jar target/calculator-server-0.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
#### 2. Compile and run the client:
|
||||
|
||||
```bash
|
||||
cd calculator-client
|
||||
mvn clean package
|
||||
java -jar target/calculator-client-0.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
## Testing the Server Manually
|
||||
|
||||
You can also test the server using a web browser or curl:
|
||||
|
||||
### Using a web browser:
|
||||
Visit: `http://localhost:8080/calculate?a=10&b=5&op=add`
|
||||
|
||||
### Using curl:
|
||||
```bash
|
||||
curl "http://localhost:8080/calculate?a=10&b=5&op=add" -H "Accept: text/event-stream"
|
||||
```
|
||||
|
||||
## Expected Output
|
||||
|
||||
When running the client, you should see streaming output similar to:
|
||||
|
||||
```
|
||||
event:info
|
||||
data:Calculating: 7.0 mul 5.0
|
||||
|
||||
event:result
|
||||
data:35.0
|
||||
```
|
||||
|
||||
## Supported Operations
|
||||
|
||||
- `add` - Addition (a + b)
|
||||
- `sub` - Subtraction (a - b)
|
||||
- `mul` - Multiplication (a * b)
|
||||
- `div` - Division (a / b, returns NaN if b = 0)
|
||||
|
||||
## API Reference
|
||||
|
||||
### GET /calculate
|
||||
|
||||
**Parameters:**
|
||||
- `a` (required): First number (double)
|
||||
- `b` (required): Second number (double)
|
||||
- `op` (required): Operation (`add`, `sub`, `mul`, `div`)
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `text/event-stream`
|
||||
- Returns Server-Sent Events with calculation progress and result
|
||||
|
||||
**Example Request:**
|
||||
```
|
||||
GET /calculate?a=7&b=5&op=mul HTTP/1.1
|
||||
Host: localhost:8080
|
||||
Accept: text/event-stream
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
```
|
||||
event: info
|
||||
data: Calculating: 7.0 mul 5.0
|
||||
|
||||
event: result
|
||||
data: 35.0
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Port 8080 already in use**
|
||||
- Stop any other applications using port 8080
|
||||
- Or change the server port in `calculator-server/src/main/resources/application.yml`
|
||||
|
||||
2. **Connection refused**
|
||||
- Make sure the server is running before starting the client
|
||||
- Check that the server started successfully on port 8080
|
||||
|
||||
3. **Parameter name issues**
|
||||
- This project includes Maven compiler configuration with `-parameters` flag
|
||||
- If you encounter parameter binding issues, ensure the project is built with this configuration
|
||||
|
||||
### Stopping the Applications
|
||||
|
||||
- Press `Ctrl+C` in the terminal where each application is running
|
||||
- Or use `mvn spring-boot:stop` if running as a background process
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **Spring Boot 3.3.1** - Application framework
|
||||
- **Spring WebFlux** - Reactive web framework
|
||||
- **Project Reactor** - Reactive streams library
|
||||
- **Netty** - Non-blocking I/O server
|
||||
- **Maven** - Build tool
|
||||
- **Java 17+** - Programming language
|
||||
|
||||
## Next Steps
|
||||
|
||||
Try modifying the code to:
|
||||
- Add more mathematical operations
|
||||
- Include error handling for invalid operations
|
||||
- Add request/response logging
|
||||
- Implement authentication
|
||||
- Add unit tests
|
||||
@@ -0,0 +1,50 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>calculator-client</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring.boot.version>3.3.1</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot WebFlux for WebClient -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<parameters>true</parameters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.example.calculatorclient;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CalculatorClientApplication implements CommandLineRunner {
|
||||
|
||||
private final WebClient client = WebClient.builder()
|
||||
.baseUrl("http://localhost:8080")
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CalculatorClientApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
client.get()
|
||||
.uri(uriBuilder -> uriBuilder
|
||||
.path("/calculate")
|
||||
.queryParam("a", 7)
|
||||
.queryParam("b", 5)
|
||||
.queryParam("op", "mul")
|
||||
.build())
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(String.class)
|
||||
.doOnNext(System.out::println)
|
||||
.blockLast();
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
spring:
|
||||
main:
|
||||
web-application-type: none
|
||||
@@ -0,0 +1,50 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>calculator-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring.boot.version>3.3.1</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot WebFlux for SSE -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<parameters>true</parameters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.example.calculatorserver;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
|
||||
@RestController
|
||||
public class CalculatorController {
|
||||
|
||||
@GetMapping(value = "/calculate", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<ServerSentEvent<String>> calculate(@RequestParam double a,
|
||||
@RequestParam double b,
|
||||
@RequestParam String op) {
|
||||
|
||||
double result;
|
||||
switch (op) {
|
||||
case "add":
|
||||
result = a + b;
|
||||
break;
|
||||
case "sub":
|
||||
result = a - b;
|
||||
break;
|
||||
case "mul":
|
||||
result = a * b;
|
||||
break;
|
||||
case "div":
|
||||
result = b != 0 ? a / b : Double.NaN;
|
||||
break;
|
||||
default:
|
||||
result = Double.NaN;
|
||||
}
|
||||
|
||||
return Flux.<ServerSentEvent<String>>just(
|
||||
ServerSentEvent.<String>builder()
|
||||
.event("info")
|
||||
.data("Calculating: " + a + " " + op + " " + b)
|
||||
.build(),
|
||||
ServerSentEvent.<String>builder()
|
||||
.event("result")
|
||||
.data(String.valueOf(result))
|
||||
.build()
|
||||
)
|
||||
.delayElements(Duration.ofSeconds(1));
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.example.calculatorserver;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CalculatorServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CalculatorServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
server:
|
||||
port: 8080
|
||||
Reference in New Issue
Block a user