Languages
[Edit]
EN

Spring Boot 3 - server-sent events example using Spring MVC

10 points
Created by:
Selina-Miranda
737

In this article, we would like to show how to use server-sent events with Spring MVC under Spring Boot 3.

In the example project we used HTTP/2 protocol for better performance.

 

Warning from MDN:

When not used over HTTP/2, SSE suffers from a limitation to the maximum number of open connections, which can be specially painful when opening various tabs as the limit is per browser and set to a very low number (6). The issue has been marked as "Won't fix" in Chrome and Firefox. This limit is per browser + domain, so that means that you can open 6 SSE connections across all of the tabs to www.example1.com and another 6 SSE connections to www.example2.com. (from Stackoverflow). When using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100).

Source: https://developer.mozilla.org/en-US/docs/Web/API/EventSource

 

Project structure

Server-sent events project created using Express.js.
Server-sent events project created using Express.js.

 

Project generator

Project was generated using this configuration (by using GENERATE button it is possible to download the project).

 

Project files

To generate src/main/resources/cert.pem and src/main/resources/key.pem files use the following command:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'

Hint: to know how to generate certificate files under Windows read this article.

 

src/main/resources/application.properties file:

server.port=8443

server.ssl.enabled=true
server.ssl.certificate=classpath:cert.pem
server.ssl.certificate-private-key=classpath:key.pem

server.http2.enabled=true

 

src/main/resources/static/index.html file:

<!doctype html>
<html>
<body>
  <script>

    const source = new EventSource('/events');  // OR:  https://localhost:8443/events

    source.onopen = (e) => console.log('Event source stream open!');
    source.onerror = (e) => console.error('Event source stream error!');

    // Handling specific events:

    source.addEventListener('my-message', (e) => console.log('Message: ' + e.data));

    // Add here more source.addEventListener(...) calls for different events ...

  </script>
</body>
</html>

Note: event source can be closed by source.close() method call.

 

src/main/java/com/example/demo/DemoApplication.java file:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

 

src/main/java/com/example/demo/MainController.java file:

package com.example.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter.SseEventBuilder;

@Controller
public class MainController {
    
    private ExecutorService executor = Executors.newCachedThreadPool();

    @RequestMapping(
        value = "/",
        method = RequestMethod.GET
    )
    public String index() {
        return "index.html";
    }

    @RequestMapping(
        value = "/events",
        method = RequestMethod.GET
    )
    public SseEmitter events() {
        SseEmitter emitter = new SseEmitter();  // 1 second timeout
        emitter.onCompletion(() -> System.out.println("Event source stream closed!"));
        emitter.onTimeout(() -> System.out.println("Event source stream timeout!"));
        emitter.onError((Throwable ex) -> System.out.println("Event source stream error!"));
        this.executor.execute(() -> {
            int counter = 0;
            try {
                while (true) {
                    String id = Long.toString(++counter);
                    SseEventBuilder event = SseEmitter.event()
                        .id(id)
                        .name("my-message")
                        .data("Some text message here ...");
                    emitter.send(event);
                    Thread.sleep(1000);
                }
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });
        return emitter;
    }
}

 

pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.2.0</version>
		<relativePath/>
	</parent>

	<groupId>com.example</groupId>
	<artifactId>server-sent-events</artifactId>

	<version>0.0.1-SNAPSHOT</version>
	<name>server-sent-events</name>
	<description>Server-sent events example using Spring Boot project.</description>

	<properties>
		<java.version>17</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

 

Project compilation

To download dependencies and compile project use the following command:

mvn clean compile

 

Server running

To run server use the following command:

mvn exec:java -Dexec.mainClass=com.example.demo.DemoApplication

Then, to see the final effect, open the link in the web browser: https://localhost:8443

 

Example preview:

Server-sent events application tested under web browser.
Server-sent events application tested under web browser.

 

See also

  1. OpenSSL - generate localhost *.pem certificate under Windows

References

  1. Server-sent events - MDN Docs
  2. EventSource - MDN Docs

Alternative titles

  1. Spring Boot 3 - server-sent events example
  2. Spring Boot 3 - server events example
  3. Spring Boot 3 - server events example using Spring MVC
  4. Spring Boot 3 - SSE example using Spring MVC
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join