fix: GREEN - Fixed NPE in `ShardedChatHome.getChatRoom()` for foreign shard
authorKai Moritz <kai@juplo.de>
Fri, 18 Aug 2023 12:09:02 +0000 (14:09 +0200)
committerKai Moritz <kai@juplo.de>
Fri, 18 Aug 2023 15:18:41 +0000 (17:18 +0200)
* `ShardedChatHome.getChatRoom(UUID)` know checks, if a `ChatHome` exists
  for the selected shard.
* If no `ChatHome` exists, a `ShardNotOwnedException` is thrown.
* The `ChatBackendControllerAdvice` translates the exception to an error
  of type 404 - NOT FOUND, to fullfill the defined expectations.

src/main/java/de/juplo/kafka/chat/backend/api/ChatBackendControllerAdvice.java
src/main/java/de/juplo/kafka/chat/backend/domain/ShardNotOwnedException.java [new file with mode: 0644]
src/main/java/de/juplo/kafka/chat/backend/domain/ShardedChatHome.java

index 55350f1..ad90c4b 100644 (file)
@@ -2,6 +2,7 @@ package de.juplo.kafka.chat.backend.api;
 
 import de.juplo.kafka.chat.backend.domain.InvalidUsernameException;
 import de.juplo.kafka.chat.backend.domain.MessageMutationException;
+import de.juplo.kafka.chat.backend.domain.ShardNotOwnedException;
 import de.juplo.kafka.chat.backend.domain.UnknownChatroomException;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpStatus;
@@ -50,6 +51,36 @@ public class ChatBackendControllerAdvice
     return problem;
   }
 
+  @ExceptionHandler(ShardNotOwnedException.class)
+  public final ProblemDetail handleException(
+      ShardNotOwnedException e,
+      ServerWebExchange exchange,
+      UriComponentsBuilder uriComponentsBuilder)
+  {
+    final HttpStatus status = HttpStatus.NOT_FOUND;
+    ProblemDetail problem = ProblemDetail.forStatus(status);
+
+    problem.setProperty("timestamp", new Date());
+
+    problem.setProperty("requestId", exchange.getRequest().getId());
+
+    problem.setType(uriComponentsBuilder.replacePath(contextPath).path("/problem/shard-not-owned").build().toUri());
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append(status.getReasonPhrase());
+    stringBuilder.append(" - ");
+    stringBuilder.append(e.getMessage());
+    problem.setTitle(stringBuilder.toString());
+
+    stringBuilder.setLength(0);
+    stringBuilder.append("Shard not owned: ");
+    stringBuilder.append(e.getShard());
+    problem.setDetail(stringBuilder.toString());
+
+    problem.setProperty("shard", e.getShard());
+
+    return problem;
+  }
+
   @ExceptionHandler(MessageMutationException.class)
   public final ProblemDetail handleException(
       MessageMutationException e,
diff --git a/src/main/java/de/juplo/kafka/chat/backend/domain/ShardNotOwnedException.java b/src/main/java/de/juplo/kafka/chat/backend/domain/ShardNotOwnedException.java
new file mode 100644 (file)
index 0000000..3b63833
--- /dev/null
@@ -0,0 +1,17 @@
+package de.juplo.kafka.chat.backend.domain;
+
+import lombok.Getter;
+
+
+public class ShardNotOwnedException extends IllegalStateException
+{
+  @Getter
+  private final int shard;
+
+
+  public ShardNotOwnedException(int shard)
+  {
+    super("This instance does not own the shard " + shard);
+    this.shard = shard;
+  }
+}
index 4b8c7f1..6d2f079 100644 (file)
@@ -40,7 +40,10 @@ public class ShardedChatHome implements ChatHome
   @Override
   public Mono<ChatRoom> getChatRoom(UUID id)
   {
-    return chatHomes[selectShard(id)].getChatRoom(id);
+    int shard = selectShard(id);
+    if (chatHomes[shard] == null)
+      throw new ShardNotOwnedException(shard);
+    return chatHomes[shard].getChatRoom(id);
   }
 
   @Override