Categories
Finance Finance and Investing Java Java For Finance Spring Boot

Getting Stock Data with Java & Spring Boot | Java For Finance

https://youtu.be/aDpjiCLr4KM

Get the code to start the project here:


Categories
Java Spring Boot

Setting Up Cucumber on Spring Boot Java Projects

https://youtu.be/OqhTQSJTeOs

Get the start files here:



Categories
Java Javascript

Learn Server Sent Events (SSE) with Java, Spring Boot & WebFlux

https://www.youtube.com/watch?v=lQCkZA-wE4U

In this tutorial, we are going to explore and learn server sent events (SSE) with Java, Spring Boot and Javascript.

The Spring dependencies we will use for this project are Spring starter web, Spring starter webflux and Spring starter thymeleaf.

In order to follow along with this Spring Boot Server Sent Events tutorial, you will need to download the start files below. The reason for this is that there is no point adding some of the boilerplate code such as the index.html file to this tutorial. We’re better off just focusing on the our Java Spring Boot implementation of Server Sent events.

Download the start point files for this project here:


What are server sent events?

Server sent events are way of communicating with clients over HTTP. As the name suggests, this form of communication derives from the server doing the the sending of information. This is in contrast to a typical REST request/response implementation where the client would send a request to a server for information and the server would upon receiving this request would return the information requested: for example this very webpage was served to you in this way. You requested the webpage from the server and the server then returned it you. Once you have got the webpage back from the server, that’s it. It’s loaded. With server sent events however the server can still send updates, which can then be processed in your web browser leading to you seeing live updates on the page. The reason this can work is because there is a persistent connection between the client and the server.

SSE are commonly compared with Websockets, which are a two-way communication protocol. SSE however, are only one-way.

Why use server sent events?

Server sent events are an excellent choice when you have the requirement to have live updates from the server, but your client doesn’t need to return any information to the server. For example, a web app like Google Finance, would make a good use case for server sent events because the users want to get constant updates of the latest stock and financial prices that the server has, but they are not updating any of that information themselves.

Consider another example, a stock trading web app, this arguably would be better suited to a communication technology like websockets, because the users want live updating of information about financial instruments, but they also want to send information about their orders to the server. There is a two-way communication between the client and the server, which makes server sent events not the most suitable choice for this application. If server sent events was chosen for this application, then it would have to paired with something else, like standard request/response endpoints for the submitting of orders in order to get the two-way communication that the trading app requires.

The Code

Controller.java

package com.example.server.sent.events.web;

import com.example.server.sent.events.model.Point;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.Collections;

@RestController
public class Controller
{
   @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Point> stream()
   {
       return Flux.interval(Duration.ofMillis(500))
               .map(interval -> Collections.singletonList(new Point()))
               .flatMapIterable(x -> x);
   }
}

Point.java

All this class is doing is allowing us to create random points that have latitude and longitude values within the UK.

package com.example.server.sent.events.model;

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import java.util.concurrent.ThreadLocalRandom;

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Point
{
    private static final Double MIN_LAT = 50.10319;
    private static final Double MAX_LAT = 60.15456;
    private static final Double MIN_LONG = 0.0;
    private static final Double MAX_LONG = 7.64133;

    private final Double latitude;
    private final Double longitude;

    public Point()
    {
        this.latitude = ThreadLocalRandom.current().nextDouble(MIN_LAT, MAX_LAT);
        this.longitude = ThreadLocalRandom.current().nextDouble(MIN_LONG, MAX_LONG) * -1.0;
    }
}

Javascript:

let mymap

class Stream {
    constructor(endpoint) {
        this.endpoint = endpoint
        this.source = null
    }

    start() {

        this.source = new EventSource(this.endpoint)

        this.source.addEventListener('message', handleEvent)

        this.source.onerror = () => {
            this.close()
        }

    }

    end() {
        this.source.close()
    }
}

const handleEvent = (event) => {
    const point = JSON.parse(event.data)
    console.log(event.data)
    addPoint(point)
}

const stream = new Stream('/stream')

window.onload = () => {
    addMap()
    stream.start()
}
window.onbeforeunload = () => {
    stream.end()
}

const addMap = () => {
  const mapdiv = document.createElement('div')
  mapdiv.id = 'mapid'
  mapdiv.setAttribute("style", "height: 1000px")
  document.body.appendChild(mapdiv)
  createMap()
}

const createMap = () => {
    mymap = L.map('mapid').setView([53.3811, 1.4701], 6)

    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(mymap)

}

const addPoint = (point) => {
    const marker = L.marker([point.latitude, point.longitude])
    marker.addTo(mymap)
}