Skip to content

Commit

Permalink
Fix stupid webflux semantic difference
Browse files Browse the repository at this point in the history
  • Loading branch information
simonharrer committed Oct 23, 2021
1 parent 880e44b commit 41ae248
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 100 deletions.
75 changes: 0 additions & 75 deletions src/main/java/sh/mob/timer/RoomRepository.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/sh/mob/timer/TimerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.reactive.config.EnableWebFlux;

@SpringBootApplication
public class TimerApplication {

public static void main(String[] args) {
System.out.println("STARTING");
SpringApplication.run(TimerApplication.class, args);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package sh.mob.timer;
package sh.mob.timer.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/")
@RequestMapping({"/", ""})
public class IndexController {

private final RoomRepository roomRepository;
Expand All @@ -19,15 +20,16 @@ public IndexController(RoomRepository roomRepository) {

@GetMapping
public String index(Model model) {
System.out.println("INDEX");
model.addAttribute("numberOfRooms", roomRepository.count());
model.addAttribute("numberOfUsers", roomRepository.countUsers());
model.addAttribute("numberOfConnections", 0);
return "index";
}

@PostMapping
public String post(@RequestParam("room") String room) {
System.out.println("POST");
return "redirect:/" + room;
public String post(@ModelAttribute Form form) {
return "redirect:/%s".formatted(form.room());
}

public record Form (String room) {}
}
58 changes: 58 additions & 0 deletions src/main/java/sh/mob/timer/web/Room.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package sh.mob.timer.web;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

final class Room {

private final String name;
private final List<TimerRequest> timerRequests = new CopyOnWriteArrayList<>();

Room(String name) {
this.name = name;
}

public void add(Long timer, String user) {
timerRequests.add(new TimerRequest(timer, Instant.now(), user));
}

public TimeLeft timeLeft() {
if (timerRequests.isEmpty()) {
return new TimeLeft(Duration.ZERO, null, null, null);
}

var lastTimerRequest = timerRequests.get(timerRequests.size() - 1);
var lastTimerRequestedTimestamp = lastTimerRequest.requested;
var timer = lastTimerRequest.timer();
var result =
Duration.between(
Instant.now(), lastTimerRequestedTimestamp.plus(timer, ChronoUnit.MINUTES));

if (result.isNegative()) {
return new TimeLeft(
Duration.ZERO, timer, lastTimerRequestedTimestamp, lastTimerRequest.user());
}

return new TimeLeft(result, timer, lastTimerRequestedTimestamp, lastTimerRequest.user());
}

public record TimeLeft(Duration duration, Long timer, Instant requested, String name) {}

public List<String> team() {
return timerRequests().stream().map(TimerRequest::user).distinct().sorted().toList();
}

public String name() {
return name;
}

public List<TimerRequest> timerRequests() {
return Collections.unmodifiableList(timerRequests);
}

record TimerRequest(Long timer, Instant requested, String user) {}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package sh.mob.timer;
package sh.mob.timer.web;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
Expand All @@ -17,6 +15,7 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import sh.mob.timer.web.Room.TimeLeft;

@RestController
@RequestMapping()
Expand All @@ -30,43 +29,55 @@ public RoomApiController(RoomRepository roomRepository) {

@GetMapping
@RequestMapping(value = "/{roomId}/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> subscribeToEvents(@PathVariable String roomId,
ServerHttpResponse response) {
response.getHeaders().setCacheControl("no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0");
public Flux<ServerSentEvent<String>> subscribeToEvents(
@PathVariable String roomId, ServerHttpResponse response) {
response
.getHeaders()
.setCacheControl("no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0");
response.getHeaders().add("X-Accel-Buffering", "no");
response.getHeaders().setConnection("keep-alive");
var room = roomRepository.get(roomId);

AtomicReference<Duration> durationBefore = new AtomicReference<>();
var durationBefore = new AtomicReference<Duration>();

return Flux.interval(Duration.ofMillis(250L))
.flatMap(
sequence -> {
var durationLeft = room.timeLeft();
var timeLeft = room.timeLeft();
var durationLeft = timeLeft.duration();
boolean isFirstInterval = durationBefore.get() == null;

var wasNotZeroBefore = isFirstInterval || !durationBefore.get().isZero();
durationBefore.set(durationLeft);

if (isFirstInterval && durationLeft.isZero()) {
return Flux.fromStream(Stream.of(timerUpdate(sequence, durationLeft)));
return Flux.fromStream(Stream.of(timerUpdate(sequence, timeLeft)));
} else if (durationLeft.isZero() && wasNotZeroBefore) {
System.out.println("DURATION ZERO");
return Flux.fromStream(
Stream.of(timerUpdate(sequence, durationLeft), timerFinished(sequence)));
Stream.of(timerUpdate(sequence, timeLeft), timerFinished(sequence)));
} else if (durationLeft.isZero()) {
return Flux.empty();
} else {
return Flux.fromStream(Stream.of(timerUpdate(sequence, durationLeft)));
return Flux.fromStream(Stream.of(timerUpdate(sequence, timeLeft)));
}
});
}

private static ServerSentEvent<String> timerUpdate(Long sequence, Duration durationLeft) {
private static ServerSentEvent<String> timerUpdate(Long sequence, TimeLeft timeLeft) {
String data =
timeLeft.timer() == null
? "00:00"
: "%d:%d (%d min timer started by %s at %s)"
.formatted(
timeLeft.duration().toMinutesPart(),
timeLeft.duration().toSecondsPart(),
timeLeft.timer(),
timeLeft.name(),
timeLeft.requested().toString());
return ServerSentEvent.<String>builder()
.id(String.valueOf(sequence))
.event("TIMER_UPDATE")
.data("" + durationLeft)
.data(data)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sh.mob.timer;
package sh.mob.timer.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
Expand All @@ -16,7 +16,7 @@ public RoomController(RoomRepository roomRepository) {
}

@GetMapping
@RequestMapping("/{roomId}")
@RequestMapping(value = "/{roomId}")
public String get(@PathVariable String roomId, Model model) {
model.addAttribute("room", roomRepository.get(roomId));
return "room";
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/sh/mob/timer/web/RoomRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sh.mob.timer.web;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Repository;

@Repository
public class RoomRepository {

private final Map<String, Room> repository = new ConcurrentHashMap<>();

Room get(String room) {
return repository.computeIfAbsent(room, Room::new);
}

public long count() {
return repository.size();
}

public long countUsers() {
return repository.values().stream().mapToLong(room -> room.team().size()).sum();
}

}
1 change: 0 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

3 changes: 2 additions & 1 deletion src/main/resources/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ <h1>Mob Timer</h1>
<ul>
<li>Rooms: [[${numberOfRooms}]]</li>
<li>Users: [[${numberOfUsers}]]</li>
<li>Connections: [[${numberOfConnections}]]</li>
</ul>

<form method="post">
<input type="text" id="room" placeholder="room"/>
<label>Room <input type="text" id="room" name="room" placeholder="room"/></label>
<button type="submit">Join</button>
</form>

Expand Down

0 comments on commit 41ae248

Please sign in to comment.