completed message persistence
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -10,9 +10,11 @@ import java.util.LinkedList;
|
||||
public class EventQueue extends LinkedList<Event<?>> implements AutoCloseable, EventListener {
|
||||
|
||||
private final InetSocketAddress addr;
|
||||
private final System.Logger log;
|
||||
|
||||
public EventQueue(InetSocketAddress addr){
|
||||
this.addr = addr;
|
||||
log = System.getLogger(addr.toString());
|
||||
messageBus().register(this);
|
||||
}
|
||||
|
||||
@@ -30,7 +32,7 @@ public class EventQueue extends LinkedList<Event<?>> implements AutoCloseable, E
|
||||
|
||||
@Override
|
||||
public void onEvent(Event<?> event) {
|
||||
System.getLogger(addr.toString()).log(System.Logger.Level.INFO,"adding event to queue of {1}: {0}",event.eventType(),addr);
|
||||
log.log(System.Logger.Level.INFO,"adding event to queue of {1}: {0}",event.eventType(),addr);
|
||||
add(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.json.JSONObject;
|
||||
public class Envelope<T extends Message<?>> {
|
||||
private final T message;
|
||||
private final Set<User> receivers;
|
||||
private final LocalDateTime time;
|
||||
private LocalDateTime time;
|
||||
private final long id;
|
||||
|
||||
public Envelope(long id, T message, User receiver){
|
||||
@@ -73,7 +73,7 @@ public class Envelope<T extends Message<?>> {
|
||||
}
|
||||
|
||||
public boolean isFor(User receiver) {
|
||||
return receivers.contains(receiver);
|
||||
return receivers.stream().anyMatch(r -> r.equals(receiver));
|
||||
}
|
||||
|
||||
public T message(){
|
||||
@@ -84,6 +84,11 @@ public class Envelope<T extends Message<?>> {
|
||||
return receivers;
|
||||
}
|
||||
|
||||
public Envelope<T> time(LocalDateTime timestamp){
|
||||
this.time = timestamp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LocalDateTime time(){
|
||||
return time;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,15 @@ public class UmbrellaUser extends User implements Mappable, Owner {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof UmbrellaUser user)) return false;
|
||||
if (o instanceof UmbrellaUser user) {
|
||||
return Objects.equals(email(), user.email())
|
||||
&& Objects.equals(name(), user.name())
|
||||
&& Objects.equals(id, user.id)
|
||||
&& Objects.equals(language(), user.language());
|
||||
}
|
||||
if (o instanceof User user) return user.equals(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
let messages = [];
|
||||
let timer = null;
|
||||
|
||||
async function display(hash){
|
||||
const url = api(`message/${hash}`);
|
||||
async function display(id){
|
||||
const url = api(`message/${id}`);
|
||||
const res = await get(url);
|
||||
if (res.ok){
|
||||
yikes();
|
||||
const json = await res.json();
|
||||
if (timer) clearTimeout(timer);
|
||||
for (let i in messages){
|
||||
if (messages[i].hash == hash){
|
||||
if (messages[i].id == id){
|
||||
messages[i].body = json.body;
|
||||
timer = setTimeout(() => {mark_read(hash)}, 2000);
|
||||
timer = setTimeout(() => {mark_read(id)}, 2000);
|
||||
} else {
|
||||
delete messages[i].body;
|
||||
}
|
||||
@@ -40,13 +40,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function mark_read(hash){
|
||||
async function mark_read(id){
|
||||
timer = null;
|
||||
const url = api(`message/read/${hash}`);
|
||||
const url = api(`message/read/${id}`);
|
||||
const res = await patch(url);
|
||||
if (res.ok) {
|
||||
for (let i in messages){
|
||||
if (messages[i].hash == hash) messages[i].read = true;
|
||||
if (messages[i].id == id) messages[i].read = true;
|
||||
}
|
||||
yikes();
|
||||
} else {
|
||||
@@ -73,7 +73,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each messages as message}
|
||||
<tr class="message-{message.hash}" onclick={ev => display(message.hash)}>
|
||||
<tr class="message-{message.id}" onclick={ev => display(message.id)}>
|
||||
<td>
|
||||
{#if message.read}→ {/if}
|
||||
{message.timestamp}
|
||||
@@ -82,7 +82,7 @@
|
||||
<td>{message.subject}</td>
|
||||
</tr>
|
||||
{#if message.body}
|
||||
<tr class="message-{message.hash} body">
|
||||
<tr class="message-{message.id} body">
|
||||
<td></td>
|
||||
<td colspan="2">
|
||||
<pre>{message.body.trim()}</pre>
|
||||
|
||||
@@ -112,13 +112,13 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
||||
Optional<Token> token = SessionToken.from(ex).map(Token::of);
|
||||
var user = ModuleRegistry.userService().loadUser(token);
|
||||
if (user.isEmpty()) return unauthorized(ex);
|
||||
var head = path.pop();
|
||||
return switch (head){
|
||||
var id = path.pop();
|
||||
return switch (id){
|
||||
case null -> listMessages(ex,user.get());
|
||||
case SETTINGS -> getSettings(ex,user.get());
|
||||
default -> {
|
||||
try {
|
||||
yield getMessage(ex,user.get(),Integer.parseInt(head));
|
||||
yield getMessage(ex,user.get(),Integer.parseInt(id));
|
||||
} catch (NumberFormatException ignored) {
|
||||
|
||||
}
|
||||
@@ -152,8 +152,8 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getMessage(HttpExchange ex, UmbrellaUser user, int hash) throws IOException {
|
||||
var envelope = queue.getEnvelope(hash).filter(env -> env.isFor(user));
|
||||
private boolean getMessage(HttpExchange ex, UmbrellaUser user, long id) throws IOException {
|
||||
var envelope = queue.getEnvelope(id).filter(env -> env.isFor(user));
|
||||
if (envelope.isPresent()) return sendMessage(ex, user, envelope.get());
|
||||
return notFound(ex);
|
||||
}
|
||||
@@ -221,7 +221,7 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
||||
|
||||
try {
|
||||
var envelopes = queue.getEnvelopesFor(receiver);
|
||||
envelopes.stream().map(Envelope::message).forEach(combined::merge);
|
||||
envelopes.stream().forEach(combined::merge);
|
||||
send(combined,date);
|
||||
envelopes.forEach(env -> queue.markRead(env.hashCode(),receiver));
|
||||
} catch (Exception ex){
|
||||
@@ -329,8 +329,8 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
||||
var sender = message.sender().name();
|
||||
var subject = message.subject();
|
||||
var time = envelope.time().format(TIME_FORMATTER);
|
||||
var hash = envelope.hashCode();
|
||||
return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time,HASH,hash));
|
||||
var id = envelope.id();
|
||||
return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time,ID,id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import static de.srsoftware.tools.jdbc.Query.*;
|
||||
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
||||
import static de.srsoftware.umbrella.core.Errors.*;
|
||||
import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
|
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
|
||||
import static de.srsoftware.umbrella.core.constants.Constants.COUNT;
|
||||
import static de.srsoftware.umbrella.core.constants.Field.*;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||
@@ -19,6 +18,7 @@ import static java.time.ZoneOffset.UTC;
|
||||
|
||||
import de.srsoftware.tools.jdbc.Query;
|
||||
import de.srsoftware.umbrella.core.BaseDb;
|
||||
import de.srsoftware.umbrella.core.Util;
|
||||
import de.srsoftware.umbrella.core.constants.Text;
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import de.srsoftware.umbrella.core.model.*;
|
||||
@@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
||||
rs.close();
|
||||
|
||||
var receivers = new ArrayList<User>();
|
||||
rs = select(ALL).from(TABLE_ATTACHMENTS).where(MESSAGE_ID,equal(messageId)).exec(db);
|
||||
rs = select(ALL).from(TABLE_RECEIVERS).where(MESSAGE_ID,equal(messageId)).exec(db);
|
||||
while (rs.next()) receivers.add(new User(rs.getString(NAME),new EmailAddress(rs.getString(EMAIL)),null));
|
||||
rs.close();
|
||||
|
||||
@@ -135,9 +135,8 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
||||
if (envelope != null) return Optional.of(envelope);
|
||||
return Optional.empty();
|
||||
} catch (SQLException e) {
|
||||
throw failedToLoadObject(Text.MESSAGE,id);
|
||||
throw failedToLoadObject(Text.MESSAGE,messageId);
|
||||
}
|
||||
throw new UmbrellaException(HTTP_SERVER_ERROR,"{class}.getEnvelope({id}) not implemented!","class",getClass().getSimpleName(), ID ,id); // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,7 +162,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
||||
var messageId = rs.getLong(ID);
|
||||
var sender = userService().loadUser(rs.getLong(SENDER_USER_ID));
|
||||
var msg = new TranslatedMessage(sender,rs.getString(SUBJECT),rs.getString(BODY), attachments.get(messageId));
|
||||
var envelope = new Envelope<>(messageId, msg, user);
|
||||
var envelope = new Envelope<>(messageId, msg, user).time(Util.dateTimeOf(rs.getLong(TIMESTAMP)));
|
||||
envelopes.add(envelope);
|
||||
}
|
||||
rs.close();
|
||||
@@ -206,6 +205,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
||||
@Override
|
||||
public Optional<Envelope<TranslatedMessage>> markRead(long messageId, User user) {
|
||||
try {
|
||||
var envelope = getEnvelope(messageId);
|
||||
Query.delete().from(TABLE_RECEIVERS).where(MESSAGE_ID,equal(messageId)).where(EMAIL,equal(user.email().toString())).execute(db);
|
||||
var rs = select(COUNT).from(TABLE_RECEIVERS).where(MESSAGE_ID,equal(messageId)).exec(db);
|
||||
Integer count = null;
|
||||
@@ -216,7 +216,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
||||
delete().from(TABLE_MESSAGES).where(ID,equal(messageId)).execute(db);
|
||||
}
|
||||
|
||||
// TODO load message or fail
|
||||
return envelope;
|
||||
} catch (SQLException e) {
|
||||
throw failedToDropObject(Text.RECEIVER);
|
||||
}
|
||||
@@ -239,8 +239,10 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
||||
for (var receiver : envelope.receivers()){
|
||||
insertInto(TABLE_RECEIVERS,MESSAGE_ID,EMAIL,NAME).values(messageId,receiver.email(),receiver.name()).execute(db).close();
|
||||
}
|
||||
for (var attachment : envelope.message().attachments()){
|
||||
insertInto(TABLE_ATTACHMENTS,MESSAGE_ID,NAME,MIME,DATA).values(messageId,attachment.name(),attachment.mime(),attachment.content()).execute(db).close();
|
||||
if (envelope.message().attachments() != null) {
|
||||
for (var attachment : envelope.message().attachments()) {
|
||||
insertInto(TABLE_ATTACHMENTS, MESSAGE_ID, NAME, MIME, DATA).values(messageId, attachment.name(), attachment.mime(), attachment.content()).execute(db).close();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw failedToStoreObject(envelope).causedBy(e);
|
||||
|
||||
@@ -6,6 +6,9 @@ import static java.lang.System.Logger.Level.TRACE;
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
import de.srsoftware.umbrella.core.model.*;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
public class CombinedMessage {
|
||||
@@ -14,20 +17,24 @@ public class CombinedMessage {
|
||||
private final Set<Attachment> attachments = new HashSet<>();
|
||||
private final StringBuilder combinedBody = new StringBuilder();
|
||||
private final User receiver;
|
||||
private final String lang;
|
||||
private String combinedSubject = null;
|
||||
private final List<Message<?>> mergedMessages = new ArrayList<>();
|
||||
private final Translatable subjectForCombinedMessage;
|
||||
private UmbrellaUser sender = null;
|
||||
private static DateTimeFormatter DT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public CombinedMessage(Translatable subjectForCombinedMessage, User receiver){
|
||||
LOG.log(DEBUG,"Creating combined message for {0}…",receiver);
|
||||
this.subjectForCombinedMessage = subjectForCombinedMessage;
|
||||
this.receiver = receiver;
|
||||
this.lang = receiver.language();
|
||||
}
|
||||
|
||||
public void merge(Message<?> message) {
|
||||
LOG.log(TRACE,"Merging {0} into combined message…",message);
|
||||
var lang = receiver.language();
|
||||
public void merge(Envelope<?> envelope) {
|
||||
LOG.log(TRACE,"Merging {0} into combined message…",envelope);
|
||||
var message = envelope.message();
|
||||
|
||||
if (message instanceof TranslatableMessage tm) message = tm.translate(lang);
|
||||
var body = message.body();
|
||||
var subject = message.subject().toString();
|
||||
@@ -38,11 +45,11 @@ public class CombinedMessage {
|
||||
combinedSubject = subject;
|
||||
break;
|
||||
case 1:
|
||||
combinedBody.insert(0,format("# {0} / {1}:\n\n",sender,subject)); // insert sender and subject of first message right before the body of the first message
|
||||
combinedBody.insert(0,format("# {0} @ {1}\n→ {2}:\n\n",sender,envelope.time().format(DT_FORMAT),subject)); // insert sender and subject of first message right before the body of the first message
|
||||
combinedSubject = subjectForCombinedMessage.translate(lang);
|
||||
// no break here, we need to append the subject and content
|
||||
default:
|
||||
combinedBody.append("\n\n━━━━━━━━━━━━━━━━━━━━━\n\n# ").append(message.sender()).append(" / ").append(subject).append(":\n\n");
|
||||
combinedBody.append("\n\n━━━━━━━━━━━━━━━━━━━━━\n\n# ").append(message.sender()).append(" @ ").append(envelope.time().format(DT_FORMAT)).append("\n→ ").append(subject).append(":\n\n");
|
||||
combinedBody.append(body);
|
||||
}
|
||||
if (message.attachments() != null) attachments.addAll(message.attachments());
|
||||
|
||||
Reference in New Issue
Block a user