diff --git a/event-statistics/README.md b/event-statistics/README.md
index ad734c3ef..8f1bd6a17 100644
--- a/event-statistics/README.md
+++ b/event-statistics/README.md
@@ -27,7 +27,18 @@ This service also has its own UI where you can see the top winners and the perce
### Team Stats
Team stats are accumulated by the number of wins by heroes vs villains. It is calculated as a percentage of hero wins to villain wins.
-Team stats are then sent over the `/stats/team` [WebSocket](https://en.wikipedia.org/wiki/WebSocket) by the [`TeamStatsWebSocket`](src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java) WebSocket class. Every time a new fight event is received, the team stats are re-computed and the new hero-to-winner percentage is emitted to anyone listening on the WebSocket.
+Team stats are then sent over the `/stats/team` [WebSocket](https://en.wikipedia.org/wiki/WebSocket) by the [`TeamStatsWebSocket`](src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java) WebSocket class. Every time a new fight event is received, the team stats are re-computed and a JSON structure is emitted to anyone listening on the WebSocket.
+
+A sample payload might look like this:
+
+```json
+{
+ "heroWins": 15,
+ "villainWins": 5,
+ "numberOfFights": 20,
+ "heroWinRatio": 0.75
+}
+```
### Winner Stats
Winner stats are accumulated by the number of wins of each hero or villain.
diff --git a/event-statistics/images/event-statistics-ui.png b/event-statistics/images/event-statistics-ui.png
index e80b975ac..ca96e5f73 100644
Binary files a/event-statistics/images/event-statistics-ui.png and b/event-statistics/images/event-statistics-ui.png differ
diff --git a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/domain/TeamScore.java b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/domain/TeamScore.java
new file mode 100644
index 000000000..ece9c7a41
--- /dev/null
+++ b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/domain/TeamScore.java
@@ -0,0 +1,48 @@
+package io.quarkus.sample.superheroes.statistics.domain;
+
+import java.util.StringJoiner;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+
+/**
+ * Data class for a team score
+ *
+ * The {@link RegisterForReflection @RegisterForReflection} annotation instructs the native compilation to allow reflection access to the class. Without it, the serialization/deserialization would not work when running the native executable.
+ *
+ */
+@RegisterForReflection
+public class TeamScore {
+ private final int heroWins;
+ private final int villainWins;
+
+ public TeamScore(int heroWins, int villainWins) {
+ this.heroWins = heroWins;
+ this.villainWins = villainWins;
+ }
+
+ public int getHeroWins() {
+ return heroWins;
+ }
+
+ public int getVillainWins() {
+ return villainWins;
+ }
+
+ public int getNumberOfFights() {
+ return getHeroWins() + getVillainWins();
+ }
+
+ public double getHeroWinRatio() {
+ return ((double) this.heroWins / getNumberOfFights());
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", TeamScore.class.getSimpleName() + "[", "]")
+ .add("heroWins=" + heroWins)
+ .add("villainWins=" + villainWins)
+ .add("numberOfFights=" + getNumberOfFights())
+ .add("heroWinRatio=" + getHeroWinRatio())
+ .toString();
+ }
+}
diff --git a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsChannelHolder.java b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsChannelHolder.java
index 21b77185d..35f64ecda 100644
--- a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsChannelHolder.java
+++ b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsChannelHolder.java
@@ -5,6 +5,8 @@
import org.eclipse.microprofile.reactive.messaging.Channel;
+import io.quarkus.sample.superheroes.statistics.domain.TeamScore;
+
import io.smallrye.mutiny.Multi;
/**
@@ -18,9 +20,9 @@
class TeamStatsChannelHolder {
@Inject
@Channel("team-stats")
- Multi teamStats;
+ Multi teamStats;
- Multi getTeamStats() {
+ Multi getTeamStats() {
return this.teamStats;
}
}
diff --git a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java
index a0a7eb74e..e3a462e5d 100644
--- a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java
+++ b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java
@@ -14,7 +14,9 @@
import io.quarkus.logging.Log;
+import com.fasterxml.jackson.databind.ObjectMapper;
import io.smallrye.mutiny.subscription.Cancellable;
+import io.smallrye.mutiny.unchecked.Unchecked;
/**
* WebSocket endpoint for the {@code /stats/team} endpoint. Exposes the {@code team-stats} channel over the socket to anyone listening.
@@ -29,10 +31,13 @@ public class TeamStatsWebSocket {
private final List sessions = new CopyOnWriteArrayList<>();
private Cancellable cancellable;
+ @Inject
+ ObjectMapper mapper;
+
@Inject
TeamStatsChannelHolder teamStatsChannelHolder;
- @OnOpen
+ @OnOpen
public void onOpen(Session session) {
this.sessions.add(session);
}
@@ -45,8 +50,8 @@ public void onClose(Session session) {
@PostConstruct
public void subscribe() {
this.cancellable = this.teamStatsChannelHolder.getTeamStats()
- .map(ratio -> Double.toString(ratio))
- .subscribe().with(ratio -> this.sessions.forEach(session -> write(session, ratio)));
+ .map(Unchecked.function(this.mapper::writeValueAsString))
+ .subscribe().with(serialized -> this.sessions.forEach(session -> write(session, serialized)));
}
@PreDestroy
diff --git a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/SuperStats.java b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/SuperStats.java
index 5e103d34a..2c07795e1 100644
--- a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/SuperStats.java
+++ b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/SuperStats.java
@@ -8,6 +8,7 @@
import io.quarkus.sample.superheroes.fight.schema.Fight;
import io.quarkus.sample.superheroes.statistics.domain.Score;
+import io.quarkus.sample.superheroes.statistics.domain.TeamScore;
import io.smallrye.mutiny.Multi;
@@ -28,9 +29,9 @@ public class SuperStats {
*/
@Incoming("fights")
@Outgoing("team-stats")
- public Multi computeTeamStats(Multi results) {
+ public Multi computeTeamStats(Multi results) {
return results.map(this.stats::add)
- .invoke(stats -> LOGGER.debugf("Fight received. Computed the team statistics: %,.010f", stats));
+ .invoke(score -> LOGGER.debugf("Fight received. Computed the team statistics: %s", score));
}
/**
diff --git a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/TeamStats.java b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/TeamStats.java
index c50eb3436..9be4df58d 100644
--- a/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/TeamStats.java
+++ b/event-statistics/src/main/java/io/quarkus/sample/superheroes/statistics/listener/TeamStats.java
@@ -1,6 +1,7 @@
package io.quarkus.sample.superheroes.statistics.listener;
import io.quarkus.sample.superheroes.fight.schema.Fight;
+import io.quarkus.sample.superheroes.statistics.domain.TeamScore;
/**
* Object keeping track of the number of battles won by heroes and villains
@@ -12,9 +13,9 @@ class TeamStats {
/**
* Adds a {@link Fight}
* @param result The {@link Fight} received
- * @return The running percentage of battles won by heroes
+ * @return A {@link TeamScore} containing running battle stats by team
*/
- double add(Fight result) {
+ TeamScore add(Fight result) {
if (result.getWinnerTeam().equalsIgnoreCase("heroes")) {
this.heroes = this.heroes + 1;
}
@@ -22,7 +23,7 @@ class TeamStats {
this.villains = this.villains + 1;
}
- return ((double) this.heroes / (this.heroes + this.villains));
+ return new TeamScore(this.heroes, this.villains);
}
int getVillainsCount() {
diff --git a/event-statistics/src/main/resources/META-INF/resources/index.html b/event-statistics/src/main/resources/META-INF/resources/index.html
index f14ca27ca..115505196 100644
--- a/event-statistics/src/main/resources/META-INF/resources/index.html
+++ b/event-statistics/src/main/resources/META-INF/resources/index.html
@@ -53,13 +53,16 @@
- Heroes
+ Heroes
+
+ 0 battles
+
@@ -84,7 +87,7 @@
var team = new WebSocket("ws://" + host + "/stats/team");
team.onmessage = function(event) {
console.log(event.data);
- updateRatio(event.data);
+ updateTeam(JSON.parse(event.data));
};
});
@@ -96,9 +99,12 @@
});
}
- function updateRatio(ratio) {
- var percent = ratio * 100;
- $("#balance").attr("aria-valuenow", ratio * 100).attr("style", "width: " + percent + "%;");
+ function updateTeam(teamScore) {
+ var percent = teamScore.heroWinRatio * 100;
+ $("#balance").attr("aria-valuenow", percent).attr("style", "width: " + percent + "%;");
+ $("#heroProgressLabel").text("Heroes (" + teamScore.heroWins + ")");
+ $("#villainProgressLabel").text("Villains (" + teamScore.villainWins + ")")
+ $("#numBattles").text(teamScore.numberOfFights + " battles");
}