Skip to content

Commit

Permalink
Use the WebClient to stream JSON to the browser
Browse files Browse the repository at this point in the history
  • Loading branch information
bclozel committed May 19, 2017
1 parent e67457f commit 5258256
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 0 deletions.
6 changes: 6 additions & 0 deletions trading-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
<version>5.0.8</version>
</dependency>
<!-- end::webjars[] -->
<!-- tag::jacksonJSR310[] -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- end::jacksonJSR310[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.spring.workshop.tradingservice;

import java.math.BigDecimal;
import java.math.MathContext;
import java.time.Instant;

public class Quote {

private static final MathContext MATH_CONTEXT = new MathContext(2);

private String ticker;

private BigDecimal price;

private Instant instant;

public Quote() {
}

public Quote(String ticker, BigDecimal price) {
this.ticker = ticker;
this.price = price;
}

public Quote(String ticker, Double price) {
this(ticker, new BigDecimal(price, MATH_CONTEXT));
}

public String getTicker() {
return ticker;
}

public void setTicker(String ticker) {
this.ticker = ticker;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public Instant getInstant() {
return instant;
}

public void setInstant(Instant instant) {
this.instant = instant;
}

@Override
public String toString() {
return "Quote{" +
"ticker='" + ticker + '\'' +
", price=" + price +
", instant=" + instant +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.spring.workshop.tradingservice;

import reactor.core.publisher.Flux;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.reactive.function.client.WebClient;

import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;

@Controller
public class QuotesController {

@GetMapping("/quotes")
public String quotes() {
return "quotes";
}

@GetMapping(path = "/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE)
@ResponseBody
public Flux<Quote> quotesStream() {
return WebClient.create("http://localhost:8081")
.get()
.uri("/quotes")
.accept(APPLICATION_STREAM_JSON)
.retrieve()
.bodyToFlux(Quote.class)
.share()
.log("io.spring.workshop.tradingservice");
}
}
96 changes: 96 additions & 0 deletions trading-service/src/main/resources/templates/quotes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="description" content="Spring WebFlux Workshop"/>
<meta name="author" content="Violeta Georgieva and Brian Clozel"/>
<title>Spring Trading application</title>
<link rel="stylesheet" href="/webjars/bootstrap/3.3.7/css/bootstrap-theme.min.css"/>
<link rel="stylesheet" href="/webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/webjars/highcharts/5.0.8/css/highcharts.css"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">Spring Trading application</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li class="active"><a href="/quotes">Quotes</a></li>
<li><a href="/websocket">Websocket</a></li>
</ul>
</div>
</div>
</nav>
<div class="container wrapper">
<div id="chart" style="height: 400px; min-width: 310px"></div>
</div>
<script type="text/javascript" src="/webjars/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/highcharts/5.0.8/highcharts.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript">

// Setting up the chart
var chart = new Highcharts.chart('chart', {
title: {
text: 'My Stock Portfolio'
},
yAxis: {
title: {
text: 'Stock Price'
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
xAxis: {
type: 'datetime',
},
series: [{
name: 'CTXS',
data: []
}, {
name: 'MSFT',
data: []
}, {
name: 'ORCL',
data: []
}, {
name: 'RHT',
data: []
}, {
name: 'VMW',
data: []
}, {
name: 'DELL',
data: []
}]
});

// This function adds the given data point to the chart
var appendStockData = function (quote) {
chart.series
.filter(function (serie) {
return serie.name == quote.ticker
})
.forEach(function (serie) {
var shift = serie.data.length > 40;
serie.addPoint([quote.instant * 1000, quote.price], true, shift);
});
};

// The browser connects to the server and receives quotes using ServerSentEvents
// those quotes are appended to the chart as they're received
var stockEventSource = new EventSource("/quotes/feed");
stockEventSource.onmessage = function (e) {
appendStockData(JSON.parse(e.data));
};
</script>
</body>
</html>

0 comments on commit 5258256

Please sign in to comment.