17

Can somebody please provide me an example for spring boot server side events?

Basically I need to push server side events to browser. I'm using angular 2 and spring boot backend. Please provide me 1 sample example, I'm unable to find good examples.

@Controller
public class SSEController {

    private final List<SseEmitter> emitters = new ArrayList<>();

    @RequestMapping(path = "/stream", method = RequestMethod.GET)
    public SseEmitter stream() throws IOException {

        SseEmitter emitter = new SseEmitter();

        emitters.add(emitter);
        emitter.onCompletion(() -> emitters.remove(emitter));

        return emitter;
    }
}

How to push data continuously from server and how to subscribe to this event in Angular 2?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Pratap A.K
  • 4,337
  • 11
  • 42
  • 79

4 Answers4

31

Have a Spring Rest controller

SseController.java

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@RestController
public class SSEController {

    public static final List<SseEmitter> emitters = Collections.synchronizedList( new ArrayList<>());

    @RequestMapping(path = "/stream", method = RequestMethod.GET)
    public SseEmitter stream() throws IOException {

        SseEmitter emitter = new SseEmitter();

        emitters.add(emitter);
        emitter.onCompletion(() -> emitters.remove(emitter));

        return emitter;
    }
}

ServiceClass.java

public void sendSseEventsToUI(Notification notification) { //your model class
        List<SseEmitter> sseEmitterListToRemove = new ArrayList<>();
        SSEController.emitters.forEach((SseEmitter emitter) -> {
            try {
                emitter.send(notification, MediaType.APPLICATION_JSON);
            } catch (IOException e) {
                emitter.complete();
                sseEmitterListToRemove.add(emitter);
                e.printStackTrace();
            }
        });
        SSEController.emitters.removeAll(sseEmitterListToRemove);
    }

finally in Angular2 component do this

notification.component.ts

import {Component, OnInit} from '@angular/core';

declare let EventSource:any;

@Component({
    selector: 'notification-cmp',
    templateUrl: 'notification.component.html'  
})

export class NotificationComponent implements OnInit {
   connect(): void {
        let source = new EventSource('http://localhost:8080/stream');
        source.addEventListener('message', message => {
            let n: Notification; //need to have this Notification model class in angular2
            n = JSON.parse(message.data);
            console.log(message.data); 
        });
    }
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Pratap A.K
  • 4,337
  • 11
  • 42
  • 79
  • 1
    Do you have any working example, I am facing issue where I am using rabbitMq to do my heavy operation with `queue` based mechanism. Now when the consumer finishes consuming the message I want to send event to UI. I cannot find a way to create a serviceImpl or a serviceComponent which I can autowire and send events – Nagendra Singh Dec 27 '19 at 06:21
  • @NagendraSingh you can post a new question for this. It's not clear what you are expecting – Pratap A.K Feb 19 '20 at 02:37
3

The Answer from Pratap A.K is great. But to keep it a little bit cleaner, you shoud create a NotificationService which implements a Interface. Like this:

NotificationServiceImpl.java

public class NotificationServiceImpl implements NotificationService {

public static final List<SseEmitter> emitters = Collections.synchronizedList(new ArrayList<>());

@Override
public SseEmitter initSseEmitters() {

    SseEmitter emitter = new SseEmitter();
    emitters.add(emitter);
    emitter.onCompletion(() -> emitters.remove(emitter));

    return emitter;
}

@Override
public void sendSseEventsToUI(WebSource notification) {
    List<SseEmitter> sseEmitterListToRemove = new ArrayList<>();
    this.emitters.forEach((SseEmitter emitter) -> {
        try {
            emitter.send(notification, MediaType.APPLICATION_JSON);
        } catch (IOException e) {
            emitter.complete();
            sseEmitterListToRemove.add(emitter);
            e.printStackTrace();
        }
    });
    this.emitters.removeAll(sseEmitterListToRemove);
  }
}

NotificationService.java

public interface NotificationService {

public SseEmitter initSseEmitters();
public void sendSseEventsToUI(WebSource notification);

}

SSEController.java

@RestController
@RequestMapping("/mystream")
public class SSEController {

@Autowired
NotificationServiceImpl INotificationServiceImpl;

@CrossOrigin
@RequestMapping(path = "/streamsource", method = RequestMethod.GET)
public SseEmitter stream() throws IOException {

    return INotificationServiceImpl.initSseEmitters();
  }
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
iSmo
  • 190
  • 3
  • 11
  • Where `sendSSeEvetnsToUI` should be called? With this code If I call the `/streamsource` controller nothing happend! How can I create a message to send it? Can you explaine more clearly? – CoderJammer Jun 24 '21 at 12:52
1

The above answer was a great help.

And..

To receive actual data pushed..

the code should be

source.onmessage = (message)=>{
   let n:Notification = JSON.parse(message.data);
}


source.addEventListener('message', message => {
// There is no data property available on 'message' here
   let n: Notification; 
   n = JSON.parse(message.data);
   console.log(message.data); 
});
Arul Rozario
  • 679
  • 1
  • 9
  • 20
0

Now with Spring Webflux is easier to accomplish that task, just using MediaTypes like:

    @GetMapping(value = "/queue/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<EventNotification> streamEvents() {
        return managerService.streamEvents();
    }

So you can create architectures like: enter image description here

You can check a working implementation in https://github.com/htenjo/vqueue also with an RSocket example.

Hernán Tenjo
  • 179
  • 2
  • 12