From 90662b3610f46232408d4e6187b2c4f3cbb0a5c9 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Thu, 16 Jul 2020 17:04:35 +0200 Subject: [PATCH 1/5] Prepare handshake synchronization Common * Replace LocalDateTime with Instant everywhere Client * Display message creation date with system time zone in MessageControl * LocalDB#users now strictly contains Users * lastSync time stamp in LocalDB (saved per user) * isOnline parameter in save function (lastSync updated if true) * lastSync time stamp in LoginCredentials * No ClientConfig#getLoginCredentials because of missing information, moved to LoginScene * Pass LocalDB#lastSync to LoginCredentials in LoginScene Server * Explicit lastSync parameter for PersistenceManager#getPending(Group)Messages This sends the correct time stamp to the server, however the JPQL queries have yet to be adjusted. --- .../java/envoy/client/data/ClientConfig.java | 9 ----- .../java/envoy/client/data/GroupChat.java | 4 +-- .../main/java/envoy/client/data/LocalDB.java | 25 ++++++++----- .../envoy/client/data/PersistentLocalDB.java | 10 +++--- .../main/java/envoy/client/ui/Startup.java | 7 ++-- .../envoy/client/ui/controller/ChatScene.java | 3 +- .../client/ui/controller/LoginScene.java | 21 +++++++++-- .../client/ui/listcell/MessageControl.java | 13 ++++--- .../main/java/envoy/data/GroupMessage.java | 12 +++---- .../java/envoy/data/LoginCredentials.java | 22 +++++++++--- common/src/main/java/envoy/data/Message.java | 35 +++++++++--------- .../main/java/envoy/data/MessageBuilder.java | 30 ++++++++-------- .../envoy/event/GroupMessageStatusChange.java | 6 ++-- .../java/envoy/event/MessageStatusChange.java | 16 ++++----- .../main/java/envoy/server/data/Contact.java | 12 +++---- .../java/envoy/server/data/GroupMessage.java | 16 ++++----- .../main/java/envoy/server/data/Message.java | 36 +++++++++---------- .../envoy/server/data/PersistenceManager.java | 29 +++++++-------- .../src/main/java/envoy/server/data/User.java | 12 +++---- .../envoy/server/net/ConnectionManager.java | 4 +-- .../processors/GroupMessageProcessor.java | 4 +-- .../GroupMessageStatusChangeProcessor.java | 6 ++-- .../processors/LoginCredentialProcessor.java | 16 ++++----- 23 files changed, 189 insertions(+), 159 deletions(-) diff --git a/client/src/main/java/envoy/client/data/ClientConfig.java b/client/src/main/java/envoy/client/data/ClientConfig.java index 49e7892..fbbdc94 100644 --- a/client/src/main/java/envoy/client/data/ClientConfig.java +++ b/client/src/main/java/envoy/client/data/ClientConfig.java @@ -5,10 +5,8 @@ import static java.util.function.Function.identity; import java.io.File; import java.util.logging.Level; -import envoy.client.ui.Startup; import envoy.data.Config; import envoy.data.ConfigItem; -import envoy.data.LoginCredentials; /** * Implements a configuration specific to the Envoy Client with default values @@ -105,11 +103,4 @@ public class ClientConfig extends Config { * @since Envoy Client v0.3-alpha */ public boolean hasLoginCredentials() { return getUser() != null && getPassword() != null; } - - /** - * @return login credentials for the specified user name and password, without - * the registration option - * @since Envoy Client v0.3-alpha - */ - public LoginCredentials getLoginCredentials() { return new LoginCredentials(getUser(), getPassword(), false, Startup.VERSION); } } diff --git a/client/src/main/java/envoy/client/data/GroupChat.java b/client/src/main/java/envoy/client/data/GroupChat.java index aafd3a0..6592e8e 100644 --- a/client/src/main/java/envoy/client/data/GroupChat.java +++ b/client/src/main/java/envoy/client/data/GroupChat.java @@ -1,7 +1,7 @@ package envoy.client.data; import java.io.IOException; -import java.time.LocalDateTime; +import java.time.Instant; import envoy.client.net.WriteProxy; import envoy.data.Contact; @@ -46,7 +46,7 @@ public class GroupChat extends Chat { else { gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ); writeProxy - .writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, LocalDateTime.now(), sender.getID())); + .writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, Instant.now(), sender.getID())); } } } diff --git a/client/src/main/java/envoy/client/data/LocalDB.java b/client/src/main/java/envoy/client/data/LocalDB.java index 421cb38..d344d98 100644 --- a/client/src/main/java/envoy/client/data/LocalDB.java +++ b/client/src/main/java/envoy/client/data/LocalDB.java @@ -1,5 +1,6 @@ package envoy.client.data; +import java.time.Instant; import java.util.*; import envoy.data.*; @@ -20,11 +21,12 @@ import envoy.event.NameChange; */ public abstract class LocalDB { - protected User user; - protected Map users = new HashMap<>(); - protected List chats = new ArrayList<>(); - protected IDGenerator idGenerator; - protected CacheMap cacheMap = new CacheMap(); + protected User user; + protected Map users = new HashMap<>(); + protected List chats = new ArrayList<>(); + protected IDGenerator idGenerator; + protected CacheMap cacheMap = new CacheMap(); + protected Instant lastSync = Instant.EPOCH; { cacheMap.put(Message.class, new Cache<>()); @@ -42,10 +44,11 @@ public abstract class LocalDB { * Stores all users. If the client user is specified, their chats will be stored * as well. The message id generator will also be saved if present. * + * @param isOnline determines which {@code lastSync} time stamp is saved * @throws Exception if the saving process failed * @since Envoy Client v0.3-alpha */ - public void save() throws Exception {} + public void save(boolean isOnline) throws Exception {} /** * Loads all user data. @@ -77,7 +80,7 @@ public abstract class LocalDB { * @since Envoy Client v0.1-beta */ public void synchronize() { - user.getContacts().stream().filter(u -> u instanceof User && !users.containsKey(u.getName())).forEach(u -> users.put(u.getName(), u)); + user.getContacts().stream().filter(u -> u instanceof User && !users.containsKey(u.getName())).forEach(u -> users.put(u.getName(), (User) u)); users.put(user.getName(), user); // Synchronize user status data @@ -98,7 +101,7 @@ public abstract class LocalDB { * user names as keys * @since Envoy Client v0.2-alpha */ - public Map getUsers() { return users; } + public Map getUsers() { return users; } /** * @return all saved {@link Chat} objects that list the client user as the @@ -148,6 +151,12 @@ public abstract class LocalDB { */ public CacheMap getCacheMap() { return cacheMap; } + /** + * @return the time stamp when the database was last saved + * @since Envoy Client v0.2-beta + */ + public Instant getLastSync() { return lastSync; } + /** * Searches for a message by ID. * diff --git a/client/src/main/java/envoy/client/data/PersistentLocalDB.java b/client/src/main/java/envoy/client/data/PersistentLocalDB.java index 182a4c8..1b0c310 100644 --- a/client/src/main/java/envoy/client/data/PersistentLocalDB.java +++ b/client/src/main/java/envoy/client/data/PersistentLocalDB.java @@ -1,6 +1,7 @@ package envoy.client.data; import java.io.*; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; @@ -26,7 +27,7 @@ public final class PersistentLocalDB extends LocalDB { /** * Constructs an empty local database. To serialize any user-specific data to * the file system, call {@link PersistentLocalDB#initializeUserStorage()} first - * and then {@link PersistentLocalDB#save()}. + * and then {@link PersistentLocalDB#save(boolean)}. * * @param dbDir the directory in which to persist data * @throws IOException if {@code dbDir} is a file (and not a directory) @@ -57,12 +58,12 @@ public final class PersistentLocalDB extends LocalDB { } @Override - public void save() throws IOException { + public void save(boolean isOnline) throws IOException { // Save users SerializationUtils.write(usersFile, users); - // Save user data - if (user != null) SerializationUtils.write(userFile, chats, cacheMap); + // Save user data and last sync time stamp + if (user != null) SerializationUtils.write(userFile, chats, cacheMap, isOnline ? Instant.now() : lastSync); // Save id generator if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); @@ -76,6 +77,7 @@ public final class PersistentLocalDB extends LocalDB { try (var in = new ObjectInputStream(new FileInputStream(userFile))) { chats = (ArrayList) in.readObject(); cacheMap = (CacheMap) in.readObject(); + lastSync = (Instant) in.readObject(); } } diff --git a/client/src/main/java/envoy/client/ui/Startup.java b/client/src/main/java/envoy/client/ui/Startup.java index 99f163e..464b8e3 100644 --- a/client/src/main/java/envoy/client/ui/Startup.java +++ b/client/src/main/java/envoy/client/ui/Startup.java @@ -121,12 +121,13 @@ public final class Startup extends Application { @Override public void stop() { try { + logger.log(Level.INFO, "Saving local database and settings..."); + localDB.save(client.isOnline()); + Settings.getInstance().save(); + logger.log(Level.INFO, "Closing connection..."); client.close(); - logger.log(Level.INFO, "Saving local database and settings..."); - localDB.save(); - Settings.getInstance().save(); logger.log(Level.INFO, "Envoy was terminated by its user"); } catch (final Exception e) { logger.log(Level.SEVERE, "Unable to save local files: ", e); diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index 920d93f..c8c9062 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -183,12 +183,11 @@ public final class ChatScene implements Restorable { final var contact = e.get(); switch (e.getOperationType()) { case ADD: - localDB.getUsers().put(contact.getName(), contact); + if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact); Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact); Platform.runLater(() -> chatList.getItems().add(chat)); break; case REMOVE: - localDB.getUsers().remove(contact.getName()); Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().equals(contact))); break; } diff --git a/client/src/main/java/envoy/client/ui/controller/LoginScene.java b/client/src/main/java/envoy/client/ui/controller/LoginScene.java index 3f0df5b..93978e8 100644 --- a/client/src/main/java/envoy/client/ui/controller/LoginScene.java +++ b/client/src/main/java/envoy/client/ui/controller/LoginScene.java @@ -2,6 +2,7 @@ package envoy.client.ui.controller; import java.io.FileNotFoundException; import java.io.IOException; +import java.time.Instant; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; @@ -94,7 +95,8 @@ public final class LoginScene { userTextField.requestFocus(); // Perform automatic login if configured - if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials()); + if (config.hasLoginCredentials()) + performHandshake(new LoginCredentials(config.getUser(), config.getPassword(), false, Startup.VERSION, loadLastSync(config.getUser()))); } @FXML @@ -108,12 +110,13 @@ public final class LoginScene { new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait(); userTextField.getTextField().clear(); } else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), registerCheckBox.isSelected(), - Startup.VERSION)); + Startup.VERSION, loadLastSync(userTextField.getTextField().getText()))); } @FXML private void offlineModeButtonPressed() { - attemptOfflineMode(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), false, Startup.VERSION)); + attemptOfflineMode( + new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), false, Startup.VERSION, localDB.getLastSync())); } @FXML @@ -130,6 +133,18 @@ public final class LoginScene { System.exit(0); } + private Instant loadLastSync(String identifier) { + try { + localDB.loadUsers(); + localDB.setUser(localDB.getUsers().get(identifier)); + localDB.initializeUserStorage(); + localDB.loadUserData(); + } catch (Exception e) { + // User storage empty, wrong user name etc. -> default lastSync + } + return localDB.getLastSync(); + } + private void performHandshake(LoginCredentials credentials) { try { client.performHandshake(credentials, cacheMap); diff --git a/client/src/main/java/envoy/client/ui/listcell/MessageControl.java b/client/src/main/java/envoy/client/ui/listcell/MessageControl.java index 8b7115b..a6ecae5 100644 --- a/client/src/main/java/envoy/client/ui/listcell/MessageControl.java +++ b/client/src/main/java/envoy/client/ui/listcell/MessageControl.java @@ -3,6 +3,7 @@ package envoy.client.ui.listcell; import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.io.ByteArrayInputStream; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Map; import java.util.logging.Level; @@ -35,11 +36,12 @@ import envoy.util.EnvoyLog; */ public class MessageControl extends Label { - private static User client; - private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"); - private static final Map statusImages = IconUtil.loadByEnum(MessageStatus.class, 16); + private static User client; - private static final Logger logger = EnvoyLog.getLogger(MessageControl.class); + private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss") + .withZone(ZoneId.systemDefault()); + private static final Map statusImages = IconUtil.loadByEnum(MessageStatus.class, 16); + private static final Logger logger = EnvoyLog.getLogger(MessageControl.class); /** * @@ -68,7 +70,8 @@ public class MessageControl extends Label { if (message.hasAttachment()) { switch (message.getAttachment().getType()) { case PICTURE: - vbox.getChildren().add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true))); + vbox.getChildren() + .add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true))); break; case VIDEO: break; diff --git a/common/src/main/java/envoy/data/GroupMessage.java b/common/src/main/java/envoy/data/GroupMessage.java index 7fb0d4a..93b8658 100644 --- a/common/src/main/java/envoy/data/GroupMessage.java +++ b/common/src/main/java/envoy/data/GroupMessage.java @@ -1,6 +1,6 @@ package envoy.data; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Collections; import java.util.Map; @@ -16,7 +16,7 @@ public final class GroupMessage extends Message { private final Map memberStatuses; - private static final long serialVersionUID = 0L; + private static final long serialVersionUID = 1L; /** * Initializes a {@link GroupMessage} with values for all of its properties. The @@ -38,9 +38,9 @@ public final class GroupMessage extends Message { * @param forwarded whether this message was forwarded * @param memberStatuses a map of all members and their status according to this * {@link GroupMessage} - * @since Envoy Common v0.1-beta + * @since Envoy Common v0.2-beta */ - GroupMessage(long id, long senderID, long groupID, LocalDateTime creationDate, LocalDateTime receivedDate, LocalDateTime readDate, String text, + GroupMessage(long id, long senderID, long groupID, Instant creationDate, Instant receivedDate, Instant readDate, String text, Attachment attachment, MessageStatus status, boolean forwarded, Map memberStatuses) { super(id, senderID, groupID, creationDate, receivedDate, readDate, text, attachment, status, forwarded); this.memberStatuses = memberStatuses; @@ -55,10 +55,10 @@ public final class GroupMessage extends Message { setStatus(Collections.min(memberStatuses.values())); switch (getStatus()) { case RECEIVED: - setReceivedDate(LocalDateTime.now()); + setReceivedDate(Instant.now()); break; case READ: - setReadDate(LocalDateTime.now()); + setReadDate(Instant.now()); break; } } diff --git a/common/src/main/java/envoy/data/LoginCredentials.java b/common/src/main/java/envoy/data/LoginCredentials.java index 6257c10..0bef595 100644 --- a/common/src/main/java/envoy/data/LoginCredentials.java +++ b/common/src/main/java/envoy/data/LoginCredentials.java @@ -1,6 +1,7 @@ package envoy.data; import java.io.Serializable; +import java.time.Instant; /** * Contains a {@link User}'s login / registration information as well as the @@ -17,8 +18,9 @@ public final class LoginCredentials implements Serializable { private final String identifier, password, clientVersion; private final boolean registration; + private final Instant lastSync; - private static final long serialVersionUID = 2; + private static final long serialVersionUID = 3; /** * Initializes login credentials for a handshake. @@ -28,18 +30,24 @@ public final class LoginCredentials implements Serializable { * @param registration signifies that these credentials are used for user * registration instead of user login * @param clientVersion the version of the client sending these credentials - * @since Envoy Common v0.1-beta + * @param lastSync the time stamp of the last synchronization + * @since Envoy Common v0.2-beta */ - public LoginCredentials(String identifier, String password, boolean registration, String clientVersion) { + public LoginCredentials(String identifier, String password, boolean registration, String clientVersion, Instant lastSync) { this.identifier = identifier; this.password = password; this.registration = registration; this.clientVersion = clientVersion; + this.lastSync = lastSync; } @Override public String toString() { - return String.format("LoginCredentials[identifier=%s,registration=%b,clientVersion=%s]", identifier, registration, clientVersion); + return String.format("LoginCredentials[identifier=%s,registration=%b,clientVersion=%s,lastSync=%s]", + identifier, + registration, + clientVersion, + lastSync); } /** @@ -66,4 +74,10 @@ public final class LoginCredentials implements Serializable { * @since Envoy Common v0.1-beta */ public String getClientVersion() { return clientVersion; } + + /** + * @return the time stamp of the last synchronization + * @since Envoy Common v0.2-beta + */ + public Instant getLastSync() { return lastSync; } } diff --git a/common/src/main/java/envoy/data/Message.java b/common/src/main/java/envoy/data/Message.java index 040ad59..8d31bd3 100644 --- a/common/src/main/java/envoy/data/Message.java +++ b/common/src/main/java/envoy/data/Message.java @@ -1,8 +1,7 @@ package envoy.data; import java.io.Serializable; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.Instant; /** * Represents a unique message with a unique, numeric ID. Further metadata @@ -50,14 +49,14 @@ public class Message implements Serializable { private final long id, senderID, recipientID; private final boolean forwarded; - private final LocalDateTime creationDate; + private final Instant creationDate; private final String text; private final Attachment attachment; - private LocalDateTime receivedDate, readDate; + private Instant receivedDate, readDate; private MessageStatus status; - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; /** * Initializes a {@link Message} with values for all of its properties. The use @@ -75,9 +74,9 @@ public class Message implements Serializable { * @param attachment the attachment of the message, if present * @param status the current {@link MessageStatus} of the message * @param forwarded whether this message was forwarded - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - Message(long id, long senderID, long recipientID, LocalDateTime creationDate, LocalDateTime receivedDate, LocalDateTime readDate, String text, + Message(long id, long senderID, long recipientID, Instant creationDate, Instant receivedDate, Instant readDate, String text, Attachment attachment, MessageStatus status, boolean forwarded) { this.id = id; this.senderID = senderID; @@ -115,7 +114,7 @@ public class Message implements Serializable { id, senderID, recipientID, - DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss").format(creationDate), + creationDate, status, text, forwarded, @@ -142,34 +141,34 @@ public class Message implements Serializable { /** * @return the date at which this message was created - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public LocalDateTime getCreationDate() { return creationDate; } + public Instant getCreationDate() { return creationDate; } /** * @return the date at which the message has been received by the sender - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public LocalDateTime getReceivedDate() { return receivedDate; } + public Instant getReceivedDate() { return receivedDate; } /** * @param receivedDate the date at which the message has been received by the * sender - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public void setReceivedDate(LocalDateTime receivedDate) { this.receivedDate = receivedDate; } + public void setReceivedDate(Instant receivedDate) { this.receivedDate = receivedDate; } /** * @return the date at which the message has been read by the sender - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public LocalDateTime getReadDate() { return readDate; } + public Instant getReadDate() { return readDate; } /** * @param readDate at which the message has been read by the sender - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public void setReadDate(LocalDateTime readDate) { this.readDate = readDate; } + public void setReadDate(Instant readDate) { this.readDate = readDate; } /** * @return the text content of this message diff --git a/common/src/main/java/envoy/data/MessageBuilder.java b/common/src/main/java/envoy/data/MessageBuilder.java index 917adc4..82df03d 100644 --- a/common/src/main/java/envoy/data/MessageBuilder.java +++ b/common/src/main/java/envoy/data/MessageBuilder.java @@ -1,6 +1,6 @@ package envoy.data; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -23,7 +23,7 @@ public class MessageBuilder { // Properties with default values private long id; - private LocalDateTime creationDate, receivedDate, readDate; + private Instant creationDate, receivedDate, readDate; private String text; private Attachment attachment; private Message.MessageStatus status; @@ -70,7 +70,7 @@ public class MessageBuilder { public MessageBuilder(Message msg, long recipientID, IDGenerator iDGenerator) { this(msg.getRecipientID(), recipientID, iDGenerator.next()); this.attachment = msg.getAttachment(); - this.creationDate = LocalDateTime.now(); + this.creationDate = Instant.now(); this.forwarded = true; this.text = msg.getText(); this.status = MessageStatus.WAITING; @@ -83,7 +83,7 @@ public class MessageBuilder { * * * - * * * @@ -113,8 +113,8 @@ public class MessageBuilder { *
*
{@code date}{@code LocalDateTime.now()} and {@code null} for {@code receivedDate} and + * {@code Instant.now()} and {@code null} for {@code receivedDate} and * {@code readDate}
* - * - * + * + * * * * @@ -140,8 +140,8 @@ public class MessageBuilder { *
*
{@code date}{@code new Date()}{@code time stamp}{@code Instant.now()}
{@code text}
* - * - * + * + * * * * @@ -162,7 +162,7 @@ public class MessageBuilder { } private void supplyDefaults() { - if (creationDate == null) creationDate = LocalDateTime.now(); + if (creationDate == null) creationDate = Instant.now(); if (text == null) text = ""; if (status == null) status = MessageStatus.WAITING; } @@ -170,9 +170,9 @@ public class MessageBuilder { /** * @param creationDate the creation date of the {@link Message} to create * @return this {@link MessageBuilder} - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public MessageBuilder setCreationDate(LocalDateTime creationDate) { + public MessageBuilder setCreationDate(Instant creationDate) { this.creationDate = creationDate; return this; } @@ -180,9 +180,9 @@ public class MessageBuilder { /** * @param receivedDate the received date of the {@link Message} to create * @return this {@link MessageBuilder} - * @since Envoy Common v0.1-beta + * @since Envoy Common v0.2-beta */ - public MessageBuilder setReceivedDate(LocalDateTime receivedDate) { + public MessageBuilder setReceivedDate(Instant receivedDate) { this.receivedDate = receivedDate; return this; } @@ -190,9 +190,9 @@ public class MessageBuilder { /** * @param readDate the read date of the {@link Message} to create * @return this {@link MessageBuilder} - * @since Envoy Common v0.1-beta + * @since Envoy Common v0.2-beta */ - public MessageBuilder setReadDate(LocalDateTime readDate) { + public MessageBuilder setReadDate(Instant readDate) { this.readDate = readDate; return this; } diff --git a/common/src/main/java/envoy/event/GroupMessageStatusChange.java b/common/src/main/java/envoy/event/GroupMessageStatusChange.java index 01f63ae..6bf4626 100644 --- a/common/src/main/java/envoy/event/GroupMessageStatusChange.java +++ b/common/src/main/java/envoy/event/GroupMessageStatusChange.java @@ -1,6 +1,6 @@ package envoy.event; -import java.time.LocalDateTime; +import java.time.Instant; import envoy.data.GroupMessage; import envoy.data.Message.MessageStatus; @@ -27,9 +27,9 @@ public class GroupMessageStatusChange extends MessageStatusChange { * @param date the date at which the MessageStatus change occurred for * this specific member * @param memberID the ID of the group member that caused the status change - * @since Envoy Common v0.1-beta + * @since Envoy Common v0.2-beta */ - public GroupMessageStatusChange(long id, MessageStatus status, LocalDateTime date, long memberID) { + public GroupMessageStatusChange(long id, MessageStatus status, Instant date, long memberID) { super(id, status, date); this.memberID = memberID; } diff --git a/common/src/main/java/envoy/event/MessageStatusChange.java b/common/src/main/java/envoy/event/MessageStatusChange.java index 01e5c70..aa94e6d 100644 --- a/common/src/main/java/envoy/event/MessageStatusChange.java +++ b/common/src/main/java/envoy/event/MessageStatusChange.java @@ -1,6 +1,6 @@ package envoy.event; -import java.time.LocalDateTime; +import java.time.Instant; import envoy.data.Message; @@ -14,8 +14,8 @@ import envoy.data.Message; */ public class MessageStatusChange extends Event { - private final long id; - private final LocalDateTime date; + private final long id; + private final Instant date; private static final long serialVersionUID = 0L; @@ -26,9 +26,9 @@ public class MessageStatusChange extends Event { * @param status the status of the {@link Message} this event is related * to * @param date the date at which the MessageStatus change occurred - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public MessageStatusChange(long id, Message.MessageStatus status, LocalDateTime date) { + public MessageStatusChange(long id, Message.MessageStatus status, Instant date) { super(status); this.id = id; this.date = date; @@ -40,7 +40,7 @@ public class MessageStatusChange extends Event { * @param message the message from which to build the event * @since Envoy Common v0.2-alpha */ - public MessageStatusChange(Message message) { this(message.getID(), message.getStatus(), LocalDateTime.now()); } + public MessageStatusChange(Message message) { this(message.getID(), message.getStatus(), Instant.now()); } /** * @return the ID of the {@link Message} this event is related to @@ -50,9 +50,9 @@ public class MessageStatusChange extends Event { /** * @return the date at which the status change occurred - * @since Envoy Common v0.2-alpha + * @since Envoy Common v0.2-beta */ - public LocalDateTime getDate() { return date; } + public Instant getDate() { return date; } @Override public String toString() { return String.format("MessageStatusChange[id=%d,status=%s,date=%s]", id, value, date); } diff --git a/server/src/main/java/envoy/server/data/Contact.java b/server/src/main/java/envoy/server/data/Contact.java index c802837..79cd720 100644 --- a/server/src/main/java/envoy/server/data/Contact.java +++ b/server/src/main/java/envoy/server/data/Contact.java @@ -1,6 +1,6 @@ package envoy.server.data; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Set; import javax.persistence.*; @@ -28,7 +28,7 @@ public abstract class Contact { protected String name; @Column(name = "creation_date") - private LocalDateTime creationDate; + private Instant creationDate; @ManyToMany(fetch = FetchType.EAGER) protected Set contacts; @@ -92,15 +92,15 @@ public abstract class Contact { /** * @return the creationDate - * @since Envoy Server Standalone v0.1-beta + * @since Envoy Server Standalone v0.2-beta */ - public LocalDateTime getCreationDate() { return creationDate; } + public Instant getCreationDate() { return creationDate; } /** * @param creationDate the creationDate to set - * @since Envoy Server Standalone v0.1-beta + * @since Envoy Server Standalone v0.2-beta */ - public void setCreationDate(LocalDateTime creationDate) { this.creationDate = creationDate; } + public void setCreationDate(Instant creationDate) { this.creationDate = creationDate; } @Override public String toString() { return String.format("%s[id=%d,name=%s, %d contact(s)]", getClass().getSimpleName(), id, name, contacts.size()); } diff --git a/server/src/main/java/envoy/server/data/GroupMessage.java b/server/src/main/java/envoy/server/data/GroupMessage.java index 90ad823..bb96895 100644 --- a/server/src/main/java/envoy/server/data/GroupMessage.java +++ b/server/src/main/java/envoy/server/data/GroupMessage.java @@ -1,6 +1,6 @@ package envoy.server.data; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -39,7 +39,7 @@ public class GroupMessage extends Message { private Map memberMessageStatus; @Column(name = "last_status_change_date") - protected LocalDateTime lastStatusChangeDate; + protected Instant lastStatusChangeDate; /** * The constructor for a database object. @@ -55,9 +55,9 @@ public class GroupMessage extends Message { * into a * database {@link GroupMessage} * @param lastStatusChangeDate the time stamp to set - * @since Envoy Server Standalone v0.1-beta + * @since Envoy Server Standalone v0.2-beta */ - public GroupMessage(envoy.data.GroupMessage groupMessage, LocalDateTime lastStatusChangeDate) { + public GroupMessage(envoy.data.GroupMessage groupMessage, Instant lastStatusChangeDate) { super(groupMessage); memberMessageStatus = groupMessage.getMemberStatuses(); this.lastStatusChangeDate = lastStatusChangeDate; @@ -92,13 +92,13 @@ public class GroupMessage extends Message { /** * @return the date at which one of the member statuses changed last - * @since Envoy Server Standalone v0.1-beta + * @since Envoy Server Standalone v0.2-beta */ - public LocalDateTime getLastStatusChangeDate() { return lastStatusChangeDate; } + public Instant getLastStatusChangeDate() { return lastStatusChangeDate; } /** * @param date the date to set - * @since Envoy Server Standalone v0.1-beta + * @since Envoy Server Standalone v0.2-beta */ - public void setLastStatusChangeDate(LocalDateTime date) { lastStatusChangeDate = date; } + public void setLastStatusChangeDate(Instant date) { lastStatusChangeDate = date; } } diff --git a/server/src/main/java/envoy/server/data/Message.java b/server/src/main/java/envoy/server/data/Message.java index afe66c7..d99b3b0 100755 --- a/server/src/main/java/envoy/server/data/Message.java +++ b/server/src/main/java/envoy/server/data/Message.java @@ -2,7 +2,7 @@ package envoy.server.data; import static envoy.data.Message.MessageStatus.*; -import java.time.LocalDateTime; +import java.time.Instant; import javax.persistence.*; @@ -55,13 +55,13 @@ public class Message { protected Contact recipient; @Column(name = "creation_date") - protected LocalDateTime creationDate; + protected Instant creationDate; @Column(name = "received_date") - protected LocalDateTime receivedDate; + protected Instant receivedDate; @Column(name = "read_date") - protected LocalDateTime readDate; + protected Instant readDate; protected String text; protected envoy.data.Message.MessageStatus status; @@ -134,7 +134,7 @@ public class Message { * @since Envoy Server Standalone v0.1-beta */ public void received() { - receivedDate = LocalDateTime.now(); + receivedDate = Instant.now(); status = RECEIVED; } @@ -145,7 +145,7 @@ public class Message { * @since Envoy Server Standalone v0.1-beta */ public void read() { - readDate = LocalDateTime.now(); + readDate = Instant.now(); status = READ; } @@ -190,43 +190,43 @@ public class Message { /** * @return the date at which a {link envoy.data.Message} has been created - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public LocalDateTime getCreationDate() { return creationDate; } + public Instant getCreationDate() { return creationDate; } /** * @param creationDate the creation date to set - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta * @see Message#getCreationDate() */ - public void setCreationDate(LocalDateTime creationDate) { this.creationDate = creationDate; } + public void setCreationDate(Instant creationDate) { this.creationDate = creationDate; } /** * @return the date at which a {link envoy.data.Message} has been received by * the server - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public LocalDateTime getReceivedDate() { return receivedDate; } + public Instant getReceivedDate() { return receivedDate; } /** * @param receivedDate the received date to set - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta * @see Message#getReceivedDate() */ - public void setReceivedDate(LocalDateTime receivedDate) { this.receivedDate = receivedDate; } + public void setReceivedDate(Instant receivedDate) { this.receivedDate = receivedDate; } /** * @return the date at which a {link envoy.data.Message} has been read - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public LocalDateTime getReadDate() { return readDate; } + public Instant getReadDate() { return readDate; } /** * @param readDate the read date to set - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta * @see Message#getReadDate() */ - public void setReadDate(LocalDateTime readDate) { this.readDate = readDate; } + public void setReadDate(Instant readDate) { this.readDate = readDate; } /** * @return the status of a {link envoy.data.Message} diff --git a/server/src/main/java/envoy/server/data/PersistenceManager.java b/server/src/main/java/envoy/server/data/PersistenceManager.java index 3432846..7ec3524 100755 --- a/server/src/main/java/envoy/server/data/PersistenceManager.java +++ b/server/src/main/java/envoy/server/data/PersistenceManager.java @@ -1,6 +1,6 @@ package envoy.server.data; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import javax.persistence.EntityManager; @@ -38,7 +38,7 @@ public class PersistenceManager { .getOnlineUsers() .stream() .map(this::getUserByID) - .forEach(user -> { user.setStatus(UserStatus.OFFLINE); user.setLastSeen(LocalDateTime.now()); entityManager.merge(user); }); + .forEach(user -> { user.setStatus(UserStatus.OFFLINE); user.setLastSeen(Instant.now()); entityManager.merge(user); }); transaction.commit(); })); } @@ -182,31 +182,29 @@ public class PersistenceManager { * Returns all messages received while being offline or the ones that have * changed. * - * @param user the user who wants to receive his unread messages + * @param user the user who wants to receive his unread messages + * @param lastSync the time stamp of the last synchronization * @return all messages that the client does not yet have (unread messages) - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public List getPendingMessages(User user) { - return entityManager - .createNamedQuery(Message.getPending) - .setParameter("user", user) - .setParameter("lastSeen", user.getLastSeen()) - .getResultList(); + public List getPendingMessages(User user, Instant lastSync) { + return entityManager.createNamedQuery(Message.getPending).setParameter("user", user).setParameter("lastSeen", lastSync).getResultList(); } /** * Returns all groupMessages received while being offline or the ones that have * changed. * - * @param user the user who wants to receive his unread groupMessages + * @param user the user who wants to receive his unread groupMessages + * @param lastSync the time stamp of the last synchronization * @return all groupMessages that the client does not yet have (unread * groupMessages) - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public List getPendingGroupMessages(User user) { + public List getPendingGroupMessages(User user, Instant lastSync) { return entityManager.createNamedQuery(GroupMessage.getPendingGroupMsg) .setParameter("userId", user.getID()) - .setParameter("lastSeen", user.getLastSeen()) + .setParameter("lastSeen", lastSync) .getResultList(); } @@ -221,8 +219,7 @@ public class PersistenceManager { * @since Envoy Server Standalone v0.1-alpha */ public List searchUsers(String searchPhrase, long userId) { - return entityManager.createNamedQuery( - User.searchByName) + return entityManager.createNamedQuery(User.searchByName) .setParameter("searchPhrase", searchPhrase + "%") .setParameter("context", getUserByID(userId)) .getResultList(); diff --git a/server/src/main/java/envoy/server/data/User.java b/server/src/main/java/envoy/server/data/User.java index 81b4d9e..b9e3d85 100755 --- a/server/src/main/java/envoy/server/data/User.java +++ b/server/src/main/java/envoy/server/data/User.java @@ -1,6 +1,6 @@ package envoy.server.data; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Set; import java.util.stream.Collectors; @@ -66,7 +66,7 @@ public class User extends Contact { private String passwordHash; @Column(name = "last_seen") - private LocalDateTime lastSeen; + private Instant lastSeen; private UserStatus status; @@ -92,15 +92,15 @@ public class User extends Contact { /** * @return the last date the user has been online - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public LocalDateTime getLastSeen() { return lastSeen; } + public Instant getLastSeen() { return lastSeen; } /** * @param lastSeen the latest date at which the user has been online to set - * @since Envoy Server Standalone v0.1-alpha + * @since Envoy Server Standalone v0.2-beta */ - public void setLastSeen(LocalDateTime lastSeen) { this.lastSeen = lastSeen; } + public void setLastSeen(Instant lastSeen) { this.lastSeen = lastSeen; } /** * @return the status diff --git a/server/src/main/java/envoy/server/net/ConnectionManager.java b/server/src/main/java/envoy/server/net/ConnectionManager.java index e0ce7ed..99bc683 100755 --- a/server/src/main/java/envoy/server/net/ConnectionManager.java +++ b/server/src/main/java/envoy/server/net/ConnectionManager.java @@ -1,6 +1,6 @@ package envoy.server.net; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @@ -52,7 +52,7 @@ public class ConnectionManager implements ISocketIdListener { // Notify contacts of this users offline-going envoy.server.data.User user = PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID)); user.setStatus(UserStatus.OFFLINE); - user.setLastSeen(LocalDateTime.now()); + user.setLastSeen(Instant.now()); UserStatusChangeProcessor.updateUserStatus(user); // Remove the socket diff --git a/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java b/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java index 54594e3..8be1a57 100644 --- a/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java +++ b/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java @@ -2,7 +2,7 @@ package envoy.server.processors; import static envoy.data.Message.MessageStatus.*; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Collections; import java.util.logging.Logger; @@ -56,7 +56,7 @@ public class GroupMessageProcessor implements ObjectProcessor { groupMessage); try { - PersistenceManager.getInstance().addMessage(new envoy.server.data.GroupMessage(groupMessage, LocalDateTime.now())); + PersistenceManager.getInstance().addMessage(new envoy.server.data.GroupMessage(groupMessage, Instant.now())); } catch (EntityExistsException e) { logger.warning("Received a groupMessage with an ID that already exists"); } diff --git a/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java b/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java index fa3bcb2..9e1077b 100644 --- a/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java +++ b/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java @@ -2,7 +2,7 @@ package envoy.server.processors; import static envoy.data.Message.MessageStatus.READ; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,7 +42,7 @@ public class GroupMessageStatusChangeProcessor implements ObjectProcessor()); @@ -117,7 +117,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor pendingGroupMessages = PersistenceManager.getInstance().getPendingGroupMessages(user); + List pendingGroupMessages = PersistenceManager.getInstance().getPendingGroupMessages(user, credentials.getLastSync()); logger.fine("Sending " + pendingGroupMessages.size() + " pending group messages to " + user + "..."); for (var gmsg : pendingGroupMessages) { @@ -148,13 +148,13 @@ public final class LoginCredentialProcessor implements ObjectProcessor Date: Thu, 16 Jul 2020 18:32:40 +0200 Subject: [PATCH 2/5] Adjust message queries for handshake sync This causes problems with group messages as the received date is null sometimes even though the status is RECEIVED. The ReceivedMessageProcessor on the client filters out the synced messages at the moment. --- common/src/main/java/envoy/data/Contact.java | 17 +++---- common/src/main/java/envoy/data/Group.java | 5 +- common/src/main/java/envoy/data/User.java | 4 +- .../main/java/envoy/server/data/Contact.java | 2 +- .../java/envoy/server/data/GroupMessage.java | 8 ++-- .../main/java/envoy/server/data/Message.java | 32 +++++++------ .../processors/LoginCredentialProcessor.java | 46 +++++++++++-------- 7 files changed, 64 insertions(+), 50 deletions(-) diff --git a/common/src/main/java/envoy/data/Contact.java b/common/src/main/java/envoy/data/Contact.java index 9a5fd0a..3fe71da 100644 --- a/common/src/main/java/envoy/data/Contact.java +++ b/common/src/main/java/envoy/data/Contact.java @@ -17,9 +17,10 @@ import java.util.Set; */ public abstract class Contact implements Serializable { - private final long id; - private final transient Set contacts; - private String name; + protected final long id; + protected final transient Set contacts; + + protected String name; private static final long serialVersionUID = 0L; @@ -55,19 +56,13 @@ public abstract class Contact implements Serializable { */ public void setName(String name) { this.name = name; } - /** - * {@inheritDoc} - */ - @Override - public String toString() { return String.format("Contact[id=%d,name=%s, contacts=%s]", id, name, contacts); } - /** * Provides a hash code based on the ID of this contact. * * @since Envoy Common v0.1-beta */ @Override - public int hashCode() { return Objects.hash(id); } + public final int hashCode() { return Objects.hash(id); } /** * Tests equality to another object. If that object is a contact as well, @@ -77,7 +72,7 @@ public abstract class Contact implements Serializable { * @return {code true} if both objects are contacts and have identical IDs */ @Override - public boolean equals(Object obj) { + public final boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Contact)) return false; return id == ((Contact) obj).id; diff --git a/common/src/main/java/envoy/data/Group.java b/common/src/main/java/envoy/data/Group.java index 4eb6b02..53f816f 100644 --- a/common/src/main/java/envoy/data/Group.java +++ b/common/src/main/java/envoy/data/Group.java @@ -36,6 +36,9 @@ public final class Group extends Contact { */ public Group(long id, String name, Set members) { super(id, name, members); } + @Override + public String toString() { return String.format("Group[id=%d,name=%s,%d member(s)]", id, name, contacts.size()); } + private void readObject(ObjectInputStream inputStream) throws Exception { inputStream.defaultReadObject(); var contacts = Contact.class.getDeclaredField("contacts"); @@ -51,5 +54,5 @@ public final class Group extends Contact { @SuppressWarnings("unchecked") @Override - public Set getContacts() { return (Set) super.getContacts(); } + public Set getContacts() { return (Set) contacts; } } diff --git a/common/src/main/java/envoy/data/User.java b/common/src/main/java/envoy/data/User.java index 7751ce3..a423934 100644 --- a/common/src/main/java/envoy/data/User.java +++ b/common/src/main/java/envoy/data/User.java @@ -99,7 +99,9 @@ public final class User extends Contact { } @Override - public String toString() { return String.format("User[id=%d,name=%s,status=%s,contacts=%s]", getID(), getName(), status, getContacts()); } + public String toString() { + return String.format("User[id=%d,name=%s,status=%s", id, name, status) + (contacts.isEmpty() ? "]" : "," + contacts.size() + " contact(s)]"); + } /** * @return the current status of this user diff --git a/server/src/main/java/envoy/server/data/Contact.java b/server/src/main/java/envoy/server/data/Contact.java index 79cd720..43a9983 100644 --- a/server/src/main/java/envoy/server/data/Contact.java +++ b/server/src/main/java/envoy/server/data/Contact.java @@ -103,5 +103,5 @@ public abstract class Contact { public void setCreationDate(Instant creationDate) { this.creationDate = creationDate; } @Override - public String toString() { return String.format("%s[id=%d,name=%s, %d contact(s)]", getClass().getSimpleName(), id, name, contacts.size()); } + public String toString() { return String.format("%s[id=%d,name=%s,%d contact(s)]", getClass().getSimpleName(), id, name, contacts.size()); } } diff --git a/server/src/main/java/envoy/server/data/GroupMessage.java b/server/src/main/java/envoy/server/data/GroupMessage.java index bb96895..45be27d 100644 --- a/server/src/main/java/envoy/server/data/GroupMessage.java +++ b/server/src/main/java/envoy/server/data/GroupMessage.java @@ -19,10 +19,10 @@ import envoy.data.Group; @Entity @NamedQuery( name = GroupMessage.getPendingGroupMsg, - query = "SELECT m FROM GroupMessage m JOIN m.memberMessageStatus s WHERE (KEY(s) = :userId) AND ((m.creationDate > :lastSeen)" - + "OR ((m.status = envoy.data.Message$MessageStatus.RECEIVED) AND (m.receivedDate > :lastSeen))" - + "OR ((m.status = envoy.data.Message$MessageStatus.READ) AND (m.readDate > :lastSeen))" - + "OR ((m.lastStatusChangeDate > :lastSeen)))" + query = "SELECT m FROM GroupMessage m JOIN m.memberMessageStatus s WHERE KEY(s) = :userId AND (m.creationDate > :lastSeen " + + "OR m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " + + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen " + + "OR m.lastStatusChangeDate > :lastSeen)" ) public class GroupMessage extends Message { diff --git a/server/src/main/java/envoy/server/data/Message.java b/server/src/main/java/envoy/server/data/Message.java index d99b3b0..fc564a2 100755 --- a/server/src/main/java/envoy/server/data/Message.java +++ b/server/src/main/java/envoy/server/data/Message.java @@ -12,11 +12,15 @@ import envoy.data.Message.MessageStatus; import envoy.data.MessageBuilder; /** - * This class serves as a way to let Hibernate communicate with the server - * without bringing the dependency of JPA/Hibernate into the client.
- * It will be referenced as "database message" to clarify between the different - * message objects.
- *
+ * This JPA entity, which will be referred to as database message, stores the + * information contained inside a {@link envoy.data.Message} inside the + * database, while having a slightly different data layout. + *

+ * A message can be converted to a database message by using the + * {@link Message#Message(envoy.data.Message)} constructor. A database message + * can be converted to a regular message using the {@link Message#toCommon()} + * method. In both cases, the objects will not contain references to each other. + *

* Project: envoy-server-standalone
* File: Message.java
* Created: 02.01.2020
@@ -29,9 +33,14 @@ import envoy.data.MessageBuilder; @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @NamedQuery( name = Message.getPending, - query = "SELECT m FROM Message m WHERE (m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT) " - + "OR (m.sender = :user) AND ((m.status = envoy.data.Message$MessageStatus.RECEIVED) AND (m.receivedDate > :lastSeen)" - + "OR (m.status = envoy.data.Message$MessageStatus.READ) AND (m.readDate > :lastSeen))" + query = "SELECT m FROM Message m WHERE " + // Send to or by the user before last seen + + "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen " + // SENT to the user + + "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT " + // Sent by the user and RECEIVED / READ after last seen + + "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " + + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)" ) public class Message { @@ -107,17 +116,14 @@ public class Message { * message * @since Envoy Server Standalone v0.1-alpha */ - public envoy.data.Message toCommon() { - return prepareBuilder().build(); - } + public envoy.data.Message toCommon() { return prepareBuilder().build(); } /** * @return a message builder containing the state of this message * @since Envoy Server Standalone v0.1-beta */ MessageBuilder prepareBuilder() { - var builder = new MessageBuilder(sender.getID(), recipient.getID(), id).setText( - text) + var builder = new MessageBuilder(sender.getID(), recipient.getID(), id).setText(text) .setCreationDate(creationDate) .setReceivedDate(receivedDate) .setReadDate(readDate) diff --git a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java index 479e249..f7ee34e 100755 --- a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java +++ b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java @@ -123,7 +123,11 @@ public final class LoginCredentialProcessor implements ObjectProcessor - writeProxy.write(socketID, new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID))); + gmsg.getMemberMessageStatus() + .forEach((memberID, memberStatus) -> writeProxy.write(socketID, + new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID))); } // Deliver just a status change instead of the whole message From 81d4e26cc671ef112eba5ba96edc304515901cb7 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Thu, 16 Jul 2020 21:14:37 +0200 Subject: [PATCH 3/5] Remove filter from ReceivedMessageProcessor, improve handshake The user is sent after the messages to avoid receiving messages on the client while switching from handshake to normal mode. --- .../client/net/ReceivedMessageProcessor.java | 16 ++++------------ .../envoy/client/ui/controller/ChatScene.java | 4 +++- .../processors/LoginCredentialProcessor.java | 8 ++++---- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/client/src/main/java/envoy/client/net/ReceivedMessageProcessor.java b/client/src/main/java/envoy/client/net/ReceivedMessageProcessor.java index f83815c..84aa7dc 100644 --- a/client/src/main/java/envoy/client/net/ReceivedMessageProcessor.java +++ b/client/src/main/java/envoy/client/net/ReceivedMessageProcessor.java @@ -1,14 +1,11 @@ package envoy.client.net; import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; import envoy.client.event.MessageCreationEvent; import envoy.data.Message; import envoy.data.Message.MessageStatus; import envoy.event.EventBus; -import envoy.util.EnvoyLog; /** * Project: envoy-client
@@ -20,17 +17,12 @@ import envoy.util.EnvoyLog; */ public class ReceivedMessageProcessor implements Consumer { - private static final Logger logger = EnvoyLog.getLogger(ReceivedMessageProcessor.class); - @Override public void accept(Message message) { - if (message.getStatus() != MessageStatus.SENT) logger.log(Level.WARNING, "The message has the unexpected status " + message.getStatus()); - else { - // Update status to RECEIVED - message.nextStatus(); + // Update status to RECEIVED + if (message.getStatus() == MessageStatus.SENT) message.nextStatus(); - // Dispatch event - EventBus.getInstance().dispatch(new MessageCreationEvent(message)); - } + // Dispatch event + EventBus.getInstance().dispatch(new MessageCreationEvent(message)); } } diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index c8c9062..ef4e38f 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -1,5 +1,7 @@ package envoy.client.ui.controller; +import static envoy.data.Message.MessageStatus.RECEIVED; + import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.io.ByteArrayInputStream; @@ -145,7 +147,7 @@ public final class ChatScene implements Restorable { logger.log(Level.WARNING, "Could not read current chat: ", e1); } Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); }); - } else chat.incrementUnreadAmount(); + } else if (message.getRecipientID() == chat.getRecipient().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount(); // Moving chat with most recent unreadMessages to the top Platform.runLater(() -> { chatList.getItems().remove(chat); diff --git a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java index f7ee34e..391dab0 100755 --- a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java +++ b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java @@ -110,10 +110,6 @@ public final class LoginCredentialProcessor implements ObjectProcessor Date: Fri, 17 Jul 2020 00:27:00 +0200 Subject: [PATCH 4/5] Fix unread message counter A bug remains when the total status of a group message is SENT, but the individual status for the client user is RECEIVED. In this case, the counter should be incremented but isn't. --- .../java/envoy/client/ui/controller/ChatScene.java | 14 +++++++++++--- .../processors/LoginCredentialProcessor.java | 12 ++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index ef4e38f..574e036 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -138,7 +138,13 @@ public final class ChatScene implements Restorable { // Listen to received messages eventBus.register(MessageCreationEvent.class, e -> { final var message = e.get(); - localDB.getChat(message instanceof GroupMessage ? message.getRecipientID() : message.getSenderID()).ifPresent(chat -> { + + // The sender of the message is the recipient of the chat + // Exceptions: this user is the sender (sync) or group message (group is + // recipient) + final long recipientID = message instanceof GroupMessage || message.getSenderID() == localDB.getUser().getID() ? message.getRecipientID() + : message.getSenderID(); + localDB.getChat(recipientID).ifPresent(chat -> { chat.insert(message); if (chat.equals(currentChat)) { try { @@ -147,8 +153,10 @@ public final class ChatScene implements Restorable { logger.log(Level.WARNING, "Could not read current chat: ", e1); } Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); }); - } else if (message.getRecipientID() == chat.getRecipient().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount(); - // Moving chat with most recent unreadMessages to the top + // TODO: Increment unread counter for group messages with status < RECEIVED + } else if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount(); + + // Move chat with most recent unread messages to the top Platform.runLater(() -> { chatList.getItems().remove(chat); chatList.getItems().add(0, chat); diff --git a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java index 391dab0..598d2d4 100755 --- a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java +++ b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java @@ -110,8 +110,9 @@ public final class LoginCredentialProcessor implements ObjectProcessor Date: Fri, 17 Jul 2020 00:29:48 +0200 Subject: [PATCH 5/5] Turn logging off by default (ClientConfig) Logging is still enabled through the client.properties however, where console logging is set to FINER. --- client/src/main/java/envoy/client/data/ClientConfig.java | 4 ++-- client/src/main/resources/client.properties | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/envoy/client/data/ClientConfig.java b/client/src/main/java/envoy/client/data/ClientConfig.java index fbbdc94..3a18f08 100644 --- a/client/src/main/java/envoy/client/data/ClientConfig.java +++ b/client/src/main/java/envoy/client/data/ClientConfig.java @@ -38,8 +38,8 @@ public class ClientConfig extends Config { items.put("localDB", new ConfigItem<>("localDB", "db", File::new, new File("localDB"), true)); items.put("ignoreLocalDB", new ConfigItem<>("ignoreLocalDB", "nodb", Boolean::parseBoolean, false, false)); items.put("homeDirectory", new ConfigItem<>("homeDirectory", "h", File::new, new File(System.getProperty("user.home"), ".envoy"), true)); - items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.CONFIG, true)); - items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.FINEST, true)); + items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.OFF, true)); + items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.OFF, true)); items.put("user", new ConfigItem<>("user", "u", identity())); items.put("password", new ConfigItem<>("password", "pw", identity())); } diff --git a/client/src/main/resources/client.properties b/client/src/main/resources/client.properties index 609d61a..25f1ffd 100644 --- a/client/src/main/resources/client.properties +++ b/client/src/main/resources/client.properties @@ -1,3 +1,4 @@ server=localhost port=8080 -localDB=.\\localDB +localDB=localDB +consoleLevelBarrier=FINER

{@code date}{@code new Date()}{@code time stamp}{@code Instant.now()}
{@code text}