package de.juplo.kafka.chat.backend.persistence.kafka;
import de.juplo.kafka.chat.backend.domain.Message;
+import de.juplo.kafka.chat.backend.domain.MessageMutationException;
import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j;
+import org.apache.kafka.clients.producer.Producer;
+import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.TopicPartition;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.UUID;
@RequiredArgsConstructor
+@Log4j
class ChatRoomActiveMessageHandlingStrategy implements MessageHandlingStrategy
{
+ private final Producer<String, MessageTo> producer;
private final TopicPartition tp;
+ private final UUID chatRoomId;
+ private final ZoneOffset zoneOffset;
+ private final KafkaChatRoomService chatRoomService;
+
+
+ @Override
+ public Mono<Message> persistMessage(
+ Message.MessageKey key,
+ LocalDateTime timestamp,
+ String text)
+ {
+ return Mono.create(sink ->
+ {
+ ProducerRecord<String, MessageTo> record =
+ new ProducerRecord<>(
+ tp.topic(),
+ tp.partition(),
+ timestamp.toEpochSecond(zoneOffset),
+ chatRoomId.toString(),
+ MessageTo.of(key.getUsername(), key.getMessageId(), text));
+
+ producer.send(record, ((metadata, exception) ->
+ {
+ if (metadata != null)
+ {
+ // On successful send
+ {
+ // Emit new message
+ message = new Message(key, metadata.offset(), timestamp, text);
+ messages.put(message.getKey(), message);
+ }
+
+ sink.success();
+ }
+ else
+ {
+ // On send-failure
+ sink.error(exception);
+ }
+ }));
+ });
+ }
@Override
public MessageHandlingStrategy handleMessage(Message message)
{
- chatrooms[tp.partition()].put()
- return this;
}
}
import de.juplo.kafka.chat.backend.domain.ChatRoomService;
import de.juplo.kafka.chat.backend.domain.Message;
import de.juplo.kafka.chat.backend.domain.MessageMutationException;
-import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
@Slf4j
-@RequiredArgsConstructor
public class KafkaChatRoomService implements ChatRoomService
{
private final Producer<String, MessageTo> producer;
private final LinkedHashMap<Message.MessageKey, Message> messages = new LinkedHashMap<>();
- private long offset = 0l;
+ private MessageHandlingStrategy strategy;
+
+
+ public KafkaChatRoomService(
+ Producer<String, MessageTo> producer,
+ TopicPartition tp,
+ UUID chatRoomId,
+ ZoneOffset zoneOffset)
+ {
+ this.producer = producer;
+ this.tp = tp;
+ this.chatRoomId = chatRoomId;
+ this.zoneOffset = zoneOffset;
+ this.strategy = new ChatroomInactiveMessageHandlingStrategy(tp);
+ }
@Override
LocalDateTime timestamp,
String text)
{
- return Mono.create(sink ->
- {
- ProducerRecord<String, MessageTo> record =
- new ProducerRecord<>(
- tp.topic(),
- tp.partition(),
- timestamp.toEpochSecond(zoneOffset),
- chatRoomId.toString(),
- MessageTo.of(key.getUsername(), key.getMessageId(), text));
+ return strategy.persistMessage(key, timestamp, text);
+ }
- producer.send(record, ((metadata, exception) ->
+ synchronized protected void addMessage(Message message) throws MessageMutationException
+ {
+ Message existingMessage = messages.get(message.getKey());
+
+ if (existingMessage == null)
+ {
+ messages.put(existingMessage.getKey(), existingMessage);
+ }
+ else
+ {
+ if (!existingMessage.getMessageText().equals(message.getMessageText()))
{
- if (metadata != null)
- {
- // On successful send
- Message message = messages.get(key);
- if (message != null)
- {
- if (message.getMessageText().equals(text))
- {
- // Warn and emit existing message
- log.warn(
- "Keeping existing message with {}@{} for {}",
- message.getSerialNumber(),
- message.getTimestamp(), key);
- }
- else
- {
- // Emit error and abort
- sink.error(new MessageMutationException(message, text));
- return;
- }
- }
- else
- {
- // Emit new message
- message = new Message(key, metadata.offset(), timestamp, text);
- messages.put(message.getKey(), message);
- }
+ throw new MessageMutationException(existingMessage, message.getMessageText());
+ }
- sink.success();
- }
- else
- {
- // On send-failure
- sink.error(exception);
- }
- }));
- });
+ // Warn and emit existing message
+ log.warn(
+ "Keeping existing message with {}@{} for {}",
+ existingMessage.getSerialNumber(),
+ existingMessage.getTimestamp(),
+ existingMessage.getKey());
+ }
}
@Override
- public Mono<Message> getMessage(Message.MessageKey key)
+ synchronized public Mono<Message> getMessage(Message.MessageKey key)
{
return Mono.fromSupplier(() -> messages.get(key));
}
@Override
- public Flux<Message> getMessages(long first, long last)
+ synchronized public Flux<Message> getMessages(long first, long last)
{
return Flux.fromStream(messages
.values()