Merge branch 'develop' into f/system_commands

This commit is contained in:
delvh 2020-07-18 11:27:59 +02:00
commit 98521aea93
No known key found for this signature in database
GPG Key ID: 42B77E634CE94D82
40 changed files with 323 additions and 328 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>envoy-client</name> <name>client</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>

View File

@ -1,3 +0,0 @@
default.configuration=
eclipse.preferences.version=1
hibernate3.enabled=false

View File

@ -5,10 +5,8 @@ import static java.util.function.Function.identity;
import java.io.File; import java.io.File;
import java.util.logging.Level; import java.util.logging.Level;
import envoy.client.ui.Startup;
import envoy.data.Config; import envoy.data.Config;
import envoy.data.ConfigItem; import envoy.data.ConfigItem;
import envoy.data.LoginCredentials;
/** /**
* Implements a configuration specific to the Envoy Client with default values * Implements a configuration specific to the Envoy Client with default values
@ -40,8 +38,8 @@ public class ClientConfig extends Config {
items.put("localDB", new ConfigItem<>("localDB", "db", File::new, new File("localDB"), true)); 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("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("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("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.OFF, true));
items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.FINEST, true)); items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.OFF, true));
items.put("user", new ConfigItem<>("user", "u", identity())); items.put("user", new ConfigItem<>("user", "u", identity()));
items.put("password", new ConfigItem<>("password", "pw", identity())); items.put("password", new ConfigItem<>("password", "pw", identity()));
} }
@ -105,11 +103,4 @@ public class ClientConfig extends Config {
* @since Envoy Client v0.3-alpha * @since Envoy Client v0.3-alpha
*/ */
public boolean hasLoginCredentials() { return getUser() != null && getPassword() != null; } 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); }
} }

View File

@ -1,7 +1,7 @@
package envoy.client.data; package envoy.client.data;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDateTime; import java.time.Instant;
import envoy.client.net.WriteProxy; import envoy.client.net.WriteProxy;
import envoy.data.Contact; import envoy.data.Contact;
@ -46,7 +46,7 @@ public class GroupChat extends Chat {
else { else {
gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ); gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ);
writeProxy writeProxy
.writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, LocalDateTime.now(), sender.getID())); .writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, Instant.now(), sender.getID()));
} }
} }
} }

View File

@ -1,5 +1,6 @@
package envoy.client.data; package envoy.client.data;
import java.time.Instant;
import java.util.*; import java.util.*;
import envoy.data.*; import envoy.data.*;
@ -21,10 +22,11 @@ import envoy.event.NameChange;
public abstract class LocalDB { public abstract class LocalDB {
protected User user; protected User user;
protected Map<String, Contact> users = new HashMap<>(); protected Map<String, User> users = new HashMap<>();
protected List<Chat> chats = new ArrayList<>(); protected List<Chat> chats = new ArrayList<>();
protected IDGenerator idGenerator; protected IDGenerator idGenerator;
protected CacheMap cacheMap = new CacheMap(); protected CacheMap cacheMap = new CacheMap();
protected Instant lastSync = Instant.EPOCH;
{ {
cacheMap.put(Message.class, new Cache<>()); 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 * 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. * 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 * @throws Exception if the saving process failed
* @since Envoy Client v0.3-alpha * @since Envoy Client v0.3-alpha
*/ */
public void save() throws Exception {} public void save(boolean isOnline) throws Exception {}
/** /**
* Loads all user data. * Loads all user data.
@ -77,7 +80,7 @@ public abstract class LocalDB {
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
public void synchronize() { 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); users.put(user.getName(), user);
// Synchronize user status data // Synchronize user status data
@ -98,7 +101,7 @@ public abstract class LocalDB {
* user names as keys * user names as keys
* @since Envoy Client v0.2-alpha * @since Envoy Client v0.2-alpha
*/ */
public Map<String, Contact> getUsers() { return users; } public Map<String, User> getUsers() { return users; }
/** /**
* @return all saved {@link Chat} objects that list the client user as the * @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; } 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. * Searches for a message by ID.
* *

View File

@ -1,6 +1,7 @@
package envoy.client.data; package envoy.client.data;
import java.io.*; import java.io.*;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; 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 * Constructs an empty local database. To serialize any user-specific data to
* the file system, call {@link PersistentLocalDB#initializeUserStorage()} first * 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 * @param dbDir the directory in which to persist data
* @throws IOException if {@code dbDir} is a file (and not a directory) * @throws IOException if {@code dbDir} is a file (and not a directory)
@ -57,12 +58,12 @@ public final class PersistentLocalDB extends LocalDB {
} }
@Override @Override
public void save() throws IOException { public void save(boolean isOnline) throws IOException {
// Save users // Save users
SerializationUtils.write(usersFile, users); SerializationUtils.write(usersFile, users);
// Save user data // Save user data and last sync time stamp
if (user != null) SerializationUtils.write(userFile, chats, cacheMap); if (user != null) SerializationUtils.write(userFile, chats, cacheMap, isOnline ? Instant.now() : lastSync);
// Save id generator // Save id generator
if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator);
@ -76,6 +77,7 @@ public final class PersistentLocalDB extends LocalDB {
try (var in = new ObjectInputStream(new FileInputStream(userFile))) { try (var in = new ObjectInputStream(new FileInputStream(userFile))) {
chats = (ArrayList<Chat>) in.readObject(); chats = (ArrayList<Chat>) in.readObject();
cacheMap = (CacheMap) in.readObject(); cacheMap = (CacheMap) in.readObject();
lastSync = (Instant) in.readObject();
} }
} }

View File

@ -1,14 +1,11 @@
package envoy.client.net; package envoy.client.net;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import envoy.client.event.MessageCreationEvent; import envoy.client.event.MessageCreationEvent;
import envoy.data.Message; import envoy.data.Message;
import envoy.data.Message.MessageStatus; import envoy.data.Message.MessageStatus;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.util.EnvoyLog;
/** /**
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
@ -20,17 +17,12 @@ import envoy.util.EnvoyLog;
*/ */
public class ReceivedMessageProcessor implements Consumer<Message> { public class ReceivedMessageProcessor implements Consumer<Message> {
private static final Logger logger = EnvoyLog.getLogger(ReceivedMessageProcessor.class);
@Override @Override
public void accept(Message message) { 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 // Update status to RECEIVED
message.nextStatus(); if (message.getStatus() == MessageStatus.SENT) message.nextStatus();
// Dispatch event // Dispatch event
EventBus.getInstance().dispatch(new MessageCreationEvent(message)); EventBus.getInstance().dispatch(new MessageCreationEvent(message));
} }
} }
}

View File

@ -121,12 +121,13 @@ public final class Startup extends Application {
@Override @Override
public void stop() { public void stop() {
try { try {
logger.log(Level.INFO, "Saving local database and settings...");
localDB.save(client.isOnline());
Settings.getInstance().save();
logger.log(Level.INFO, "Closing connection..."); logger.log(Level.INFO, "Closing connection...");
client.close(); 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"); logger.log(Level.INFO, "Envoy was terminated by its user");
} catch (final Exception e) { } catch (final Exception e) {
logger.log(Level.SEVERE, "Unable to save local files: ", e); logger.log(Level.SEVERE, "Unable to save local files: ", e);

View File

@ -1,5 +1,7 @@
package envoy.client.ui.controller; package envoy.client.ui.controller;
import static envoy.data.Message.MessageStatus.RECEIVED;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -134,7 +136,13 @@ public final class ChatScene implements Restorable {
// Listen to received messages // Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> { eventBus.register(MessageCreationEvent.class, e -> {
final var message = e.get(); 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); chat.insert(message);
if (chat.equals(currentChat)) { if (chat.equals(currentChat)) {
try { try {
@ -143,8 +151,10 @@ public final class ChatScene implements Restorable {
logger.log(Level.WARNING, "Could not read current chat: ", e1); logger.log(Level.WARNING, "Could not read current chat: ", e1);
} }
Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); }); Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); });
} else chat.incrementUnreadAmount(); // TODO: Increment unread counter for group messages with status < RECEIVED
// Moving chat with most recent unreadMessages to the top } else if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount();
// Move chat with most recent unread messages to the top
Platform.runLater(() -> { Platform.runLater(() -> {
chatList.getItems().remove(chat); chatList.getItems().remove(chat);
chatList.getItems().add(0, chat); chatList.getItems().add(0, chat);
@ -182,12 +192,11 @@ public final class ChatScene implements Restorable {
final var contact = e.get(); final var contact = e.get();
switch (e.getOperationType()) { switch (e.getOperationType()) {
case ADD: case ADD:
localDB.getUsers().put(contact.getName(), contact); if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
final Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact); Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
Platform.runLater(() -> chatList.getItems().add(chat)); Platform.runLater(() -> chatList.getItems().add(chat));
break; break;
case REMOVE: case REMOVE:
localDB.getUsers().remove(contact.getName());
Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().equals(contact))); Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().equals(contact)));
break; break;
} }

View File

@ -2,6 +2,7 @@ package envoy.client.ui.controller;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -94,7 +95,8 @@ public final class LoginScene {
userTextField.requestFocus(); userTextField.requestFocus();
// Perform automatic login if configured // 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 @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(); new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
userTextField.getTextField().clear(); userTextField.getTextField().clear();
} else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), registerCheckBox.isSelected(), } else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), registerCheckBox.isSelected(),
Startup.VERSION)); Startup.VERSION, loadLastSync(userTextField.getTextField().getText())));
} }
@FXML @FXML
private void offlineModeButtonPressed() { 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 @FXML
@ -130,6 +133,18 @@ public final class LoginScene {
System.exit(0); 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) { private void performHandshake(LoginCredentials credentials) {
try { try {
client.performHandshake(credentials, cacheMap); client.performHandshake(credentials, cacheMap);

View File

@ -3,6 +3,7 @@ package envoy.client.ui.listcell;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@ -36,9 +37,10 @@ import envoy.util.EnvoyLog;
public class MessageControl extends Label { public class MessageControl extends Label {
private static User client; private static User client;
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
private static final Map<MessageStatus, Image> statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
.withZone(ZoneId.systemDefault());
private static final Map<MessageStatus, Image> statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
private static final Logger logger = EnvoyLog.getLogger(MessageControl.class); private static final Logger logger = EnvoyLog.getLogger(MessageControl.class);
/** /**
@ -68,7 +70,8 @@ public class MessageControl extends Label {
if (message.hasAttachment()) { if (message.hasAttachment()) {
switch (message.getAttachment().getType()) { switch (message.getAttachment().getType()) {
case PICTURE: 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; break;
case VIDEO: case VIDEO:
break; break;

View File

@ -1,3 +1,4 @@
server=localhost server=localhost
port=8080 port=8080
localDB=.\\localDB localDB=localDB
consoleLevelBarrier=FINER

View File

@ -8,15 +8,9 @@
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> <classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes> <attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> <attribute name="test" value="true"/>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
@ -24,5 +18,10 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

View File

@ -1,35 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>envoy-common</name> <name>common</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>
<buildSpec> <buildSpec>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name> <name>org.eclipse.jdt.core.javabuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand>
<name>org.jboss.tools.jst.web.kb.kbbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.jboss.tools.cdi.core.cdibuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name> <name>org.eclipse.m2e.core.maven2Builder</name>
<arguments> <arguments>
@ -37,10 +17,7 @@
</buildCommand> </buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

View File

@ -1,5 +1,4 @@
eclipse.preferences.version=1 eclipse.preferences.version=1
encoding//src/main/java=UTF-8 encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8 encoding//src/test/java=UTF-8
encoding//src/test/resources=UTF-8
encoding/<project>=UTF-8 encoding/<project>=UTF-8

View File

@ -9,8 +9,14 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=11 org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
org.eclipse.jdt.core.compiler.problem.APILeak=warning org.eclipse.jdt.core.compiler.problem.APILeak=warning
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
@ -21,10 +27,10 @@ org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=info org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
@ -35,14 +41,27 @@ org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=info
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning org.eclipse.jdt.core.compiler.problem.missingJavadocComments=info
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=info
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@ -63,12 +82,12 @@ org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
@ -79,18 +98,18 @@ org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
@ -104,7 +123,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.release=disabled
@ -226,7 +245,7 @@ org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=insert org.eclipse.jdt.core.formatter.insert_new_line_after_label=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
@ -420,7 +439,7 @@ org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=true org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=true
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_if_single_item org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_if_single_item
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_always org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_always
@ -431,7 +450,7 @@ org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=true org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=true
org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=true org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=true
org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.lineSplit=150 org.eclipse.jdt.core.formatter.lineSplit=150
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false

File diff suppressed because one or more lines are too long

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="envoy-common">
<wb-resource deploy-path="/" source-path="/src/main/java"/>
</wb-module>
</project-modules>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<installed facet="jst.utility" version="1.0"/>
<installed facet="java" version="11"/>
</faceted-project>

View File

@ -1,2 +0,0 @@
disabled=06target
eclipse.preferences.version=1

View File

@ -17,9 +17,10 @@ import java.util.Set;
*/ */
public abstract class Contact implements Serializable { public abstract class Contact implements Serializable {
private final long id; protected final long id;
private final transient Set<? extends Contact> contacts; protected final transient Set<? extends Contact> contacts;
private String name;
protected String name;
private static final long serialVersionUID = 0L; private static final long serialVersionUID = 0L;
@ -55,19 +56,13 @@ public abstract class Contact implements Serializable {
*/ */
public void setName(String name) { this.name = name; } 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. * Provides a hash code based on the ID of this contact.
* *
* @since Envoy Common v0.1-beta * @since Envoy Common v0.1-beta
*/ */
@Override @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, * 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 * @return {code true} if both objects are contacts and have identical IDs
*/ */
@Override @Override
public boolean equals(Object obj) { public final boolean equals(Object obj) {
if (this == obj) return true; if (this == obj) return true;
if (!(obj instanceof Contact)) return false; if (!(obj instanceof Contact)) return false;
return id == ((Contact) obj).id; return id == ((Contact) obj).id;

View File

@ -36,6 +36,9 @@ public final class Group extends Contact {
*/ */
public Group(long id, String name, Set<User> members) { super(id, name, members); } public Group(long id, String name, Set<User> 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 { private void readObject(ObjectInputStream inputStream) throws Exception {
inputStream.defaultReadObject(); inputStream.defaultReadObject();
var contacts = Contact.class.getDeclaredField("contacts"); var contacts = Contact.class.getDeclaredField("contacts");
@ -49,7 +52,6 @@ public final class Group extends Contact {
outputStream.writeObject(getContacts()); outputStream.writeObject(getContacts());
} }
@SuppressWarnings("unchecked")
@Override @Override
public Set<User> getContacts() { return (Set<User>) super.getContacts(); } public Set<User> getContacts() { return (Set<User>) contacts; }
} }

View File

@ -1,6 +1,6 @@
package envoy.data; package envoy.data;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@ -16,7 +16,7 @@ public final class GroupMessage extends Message {
private final Map<Long, MessageStatus> memberStatuses; private final Map<Long, MessageStatus> 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 * 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 forwarded whether this message was forwarded
* @param memberStatuses a map of all members and their status according to this * @param memberStatuses a map of all members and their status according to this
* {@link GroupMessage} * {@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<Long, MessageStatus> memberStatuses) { Attachment attachment, MessageStatus status, boolean forwarded, Map<Long, MessageStatus> memberStatuses) {
super(id, senderID, groupID, creationDate, receivedDate, readDate, text, attachment, status, forwarded); super(id, senderID, groupID, creationDate, receivedDate, readDate, text, attachment, status, forwarded);
this.memberStatuses = memberStatuses; this.memberStatuses = memberStatuses;
@ -55,10 +55,10 @@ public final class GroupMessage extends Message {
setStatus(Collections.min(memberStatuses.values())); setStatus(Collections.min(memberStatuses.values()));
switch (getStatus()) { switch (getStatus()) {
case RECEIVED: case RECEIVED:
setReceivedDate(LocalDateTime.now()); setReceivedDate(Instant.now());
break; break;
case READ: case READ:
setReadDate(LocalDateTime.now()); setReadDate(Instant.now());
break; break;
} }
} }

View File

@ -1,6 +1,7 @@
package envoy.data; package envoy.data;
import java.io.Serializable; import java.io.Serializable;
import java.time.Instant;
/** /**
* Contains a {@link User}'s login / registration information as well as the * 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 String identifier, password, clientVersion;
private final boolean registration; 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. * 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 * @param registration signifies that these credentials are used for user
* registration instead of user login * registration instead of user login
* @param clientVersion the version of the client sending these credentials * @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.identifier = identifier;
this.password = password; this.password = password;
this.registration = registration; this.registration = registration;
this.clientVersion = clientVersion; this.clientVersion = clientVersion;
this.lastSync = lastSync;
} }
@Override @Override
public String toString() { 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 * @since Envoy Common v0.1-beta
*/ */
public String getClientVersion() { return clientVersion; } public String getClientVersion() { return clientVersion; }
/**
* @return the time stamp of the last synchronization
* @since Envoy Common v0.2-beta
*/
public Instant getLastSync() { return lastSync; }
} }

View File

@ -1,8 +1,7 @@
package envoy.data; package envoy.data;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.Instant;
import java.time.format.DateTimeFormatter;
/** /**
* Represents a unique message with a unique, numeric ID. Further metadata * 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 long id, senderID, recipientID;
private final boolean forwarded; private final boolean forwarded;
private final LocalDateTime creationDate; private final Instant creationDate;
private final String text; private final String text;
private final Attachment attachment; private final Attachment attachment;
private LocalDateTime receivedDate, readDate; private Instant receivedDate, readDate;
private MessageStatus status; 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 * 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 attachment the attachment of the message, if present
* @param status the current {@link MessageStatus} of the message * @param status the current {@link MessageStatus} of the message
* @param forwarded whether this message was forwarded * @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) { Attachment attachment, MessageStatus status, boolean forwarded) {
this.id = id; this.id = id;
this.senderID = senderID; this.senderID = senderID;
@ -115,7 +114,7 @@ public class Message implements Serializable {
id, id,
senderID, senderID,
recipientID, recipientID,
DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss").format(creationDate), creationDate,
status, status,
text, text,
forwarded, forwarded,
@ -142,34 +141,34 @@ public class Message implements Serializable {
/** /**
* @return the date at which this message was created * @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 * @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 * @param receivedDate the date at which the message has been received by the
* sender * 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 * @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 * @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 * @return the text content of this message

View File

@ -1,6 +1,6 @@
package envoy.data; package envoy.data;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -23,7 +23,7 @@ public class MessageBuilder {
// Properties with default values // Properties with default values
private long id; private long id;
private LocalDateTime creationDate, receivedDate, readDate; private Instant creationDate, receivedDate, readDate;
private String text; private String text;
private Attachment attachment; private Attachment attachment;
private Message.MessageStatus status; private Message.MessageStatus status;
@ -70,7 +70,7 @@ public class MessageBuilder {
public MessageBuilder(Message msg, long recipientID, IDGenerator iDGenerator) { public MessageBuilder(Message msg, long recipientID, IDGenerator iDGenerator) {
this(msg.getRecipientID(), recipientID, iDGenerator.next()); this(msg.getRecipientID(), recipientID, iDGenerator.next());
this.attachment = msg.getAttachment(); this.attachment = msg.getAttachment();
this.creationDate = LocalDateTime.now(); this.creationDate = Instant.now();
this.forwarded = true; this.forwarded = true;
this.text = msg.getText(); this.text = msg.getText();
this.status = MessageStatus.WAITING; this.status = MessageStatus.WAITING;
@ -83,7 +83,7 @@ public class MessageBuilder {
* <table border="1"> * <table border="1">
* <tr> * <tr>
* <td>{@code date}</td> * <td>{@code date}</td>
* <td>{@code LocalDateTime.now()} and {@code null} for {@code receivedDate} and * <td>{@code Instant.now()} and {@code null} for {@code receivedDate} and
* {@code readDate}</td> * {@code readDate}</td>
* <tr> * <tr>
* <tr> * <tr>
@ -113,8 +113,8 @@ public class MessageBuilder {
* <br> * <br>
* <table border="1"> * <table border="1">
* <tr> * <tr>
* <td>{@code date}</td> * <td>{@code time stamp}</td>
* <td>{@code new Date()}</td> * <td>{@code Instant.now()}</td>
* <tr> * <tr>
* <tr> * <tr>
* <td>{@code text}</td> * <td>{@code text}</td>
@ -140,8 +140,8 @@ public class MessageBuilder {
* <br> * <br>
* <table border="1"> * <table border="1">
* <tr> * <tr>
* <td>{@code date}</td> * <td>{@code time stamp}</td>
* <td>{@code new Date()}</td> * <td>{@code Instant.now()}</td>
* <tr> * <tr>
* <tr> * <tr>
* <td>{@code text}</td> * <td>{@code text}</td>
@ -162,7 +162,7 @@ public class MessageBuilder {
} }
private void supplyDefaults() { private void supplyDefaults() {
if (creationDate == null) creationDate = LocalDateTime.now(); if (creationDate == null) creationDate = Instant.now();
if (text == null) text = ""; if (text == null) text = "";
if (status == null) status = MessageStatus.WAITING; if (status == null) status = MessageStatus.WAITING;
} }
@ -170,9 +170,9 @@ public class MessageBuilder {
/** /**
* @param creationDate the creation date of the {@link Message} to create * @param creationDate the creation date of the {@link Message} to create
* @return this {@link MessageBuilder} * @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; this.creationDate = creationDate;
return this; return this;
} }
@ -180,9 +180,9 @@ public class MessageBuilder {
/** /**
* @param receivedDate the received date of the {@link Message} to create * @param receivedDate the received date of the {@link Message} to create
* @return this {@link MessageBuilder} * @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; this.receivedDate = receivedDate;
return this; return this;
} }
@ -190,9 +190,9 @@ public class MessageBuilder {
/** /**
* @param readDate the read date of the {@link Message} to create * @param readDate the read date of the {@link Message} to create
* @return this {@link MessageBuilder} * @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; this.readDate = readDate;
return this; return this;
} }

View File

@ -99,7 +99,9 @@ public final class User extends Contact {
} }
@Override @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 * @return the current status of this user

View File

@ -53,7 +53,6 @@ public class EventBus {
* @param handler the event handler to register * @param handler the event handler to register
* @since Envoy v0.2-alpha * @since Envoy v0.2-alpha
*/ */
@SuppressWarnings("unchecked")
public <T extends Event<?>> void register(Class<T> eventClass, Consumer<T> handler) { public <T extends Event<?>> void register(Class<T> eventClass, Consumer<T> handler) {
if (!handlers.containsKey(eventClass)) handlers.put(eventClass, new ArrayList<>()); if (!handlers.containsKey(eventClass)) handlers.put(eventClass, new ArrayList<>());
handlers.get(eventClass).add((Consumer<Event<?>>) handler); handlers.get(eventClass).add((Consumer<Event<?>>) handler);

View File

@ -1,6 +1,6 @@
package envoy.event; package envoy.event;
import java.time.LocalDateTime; import java.time.Instant;
import envoy.data.GroupMessage; import envoy.data.GroupMessage;
import envoy.data.Message.MessageStatus; 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 * @param date the date at which the MessageStatus change occurred for
* this specific member * this specific member
* @param memberID the ID of the group member that caused the status change * @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); super(id, status, date);
this.memberID = memberID; this.memberID = memberID;
} }

View File

@ -1,6 +1,6 @@
package envoy.event; package envoy.event;
import java.time.LocalDateTime; import java.time.Instant;
import envoy.data.Message; import envoy.data.Message;
@ -15,7 +15,7 @@ import envoy.data.Message;
public class MessageStatusChange extends Event<Message.MessageStatus> { public class MessageStatusChange extends Event<Message.MessageStatus> {
private final long id; private final long id;
private final LocalDateTime date; private final Instant date;
private static final long serialVersionUID = 0L; private static final long serialVersionUID = 0L;
@ -26,9 +26,9 @@ public class MessageStatusChange extends Event<Message.MessageStatus> {
* @param status the status of the {@link Message} this event is related * @param status the status of the {@link Message} this event is related
* to * to
* @param date the date at which the MessageStatus change occurred * @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); super(status);
this.id = id; this.id = id;
this.date = date; this.date = date;
@ -40,7 +40,7 @@ public class MessageStatusChange extends Event<Message.MessageStatus> {
* @param message the message from which to build the event * @param message the message from which to build the event
* @since Envoy Common v0.2-alpha * @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 * @return the ID of the {@link Message} this event is related to
@ -50,9 +50,9 @@ public class MessageStatusChange extends Event<Message.MessageStatus> {
/** /**
* @return the date at which the status change occurred * @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 @Override
public String toString() { return String.format("MessageStatusChange[id=%d,status=%s,date=%s]", id, value, date); } public String toString() { return String.format("MessageStatusChange[id=%d,status=%s,date=%s]", id, value, date); }

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>envoy-server-standalone</name> <name>server</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>
@ -35,6 +35,5 @@
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature> <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.hibernate.eclipse.console.hibernateNature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

View File

@ -1,6 +1,6 @@
package envoy.server.data; package envoy.server.data;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.Set; import java.util.Set;
import javax.persistence.*; import javax.persistence.*;
@ -28,7 +28,7 @@ public abstract class Contact {
protected String name; protected String name;
@Column(name = "creation_date") @Column(name = "creation_date")
private LocalDateTime creationDate; private Instant creationDate;
@ManyToMany(fetch = FetchType.EAGER) @ManyToMany(fetch = FetchType.EAGER)
protected Set<Contact> contacts; protected Set<Contact> contacts;
@ -92,15 +92,15 @@ public abstract class Contact {
/** /**
* @return the creationDate * @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 * @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 @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()); }

View File

@ -1,6 +1,6 @@
package envoy.server.data; package envoy.server.data;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -19,10 +19,10 @@ import envoy.data.Group;
@Entity @Entity
@NamedQuery( @NamedQuery(
name = GroupMessage.getPendingGroupMsg, name = GroupMessage.getPendingGroupMsg,
query = "SELECT m FROM GroupMessage m JOIN m.memberMessageStatus s WHERE (KEY(s) = :userId) AND ((m.creationDate > :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.RECEIVED AND m.receivedDate > :lastSeen "
+ "OR ((m.status = envoy.data.Message$MessageStatus.READ) AND (m.readDate > :lastSeen))" + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen "
+ "OR ((m.lastStatusChangeDate > :lastSeen)))" + "OR m.lastStatusChangeDate > :lastSeen)"
) )
public class GroupMessage extends Message { public class GroupMessage extends Message {
@ -39,7 +39,7 @@ public class GroupMessage extends Message {
private Map<Long, envoy.data.Message.MessageStatus> memberMessageStatus; private Map<Long, envoy.data.Message.MessageStatus> memberMessageStatus;
@Column(name = "last_status_change_date") @Column(name = "last_status_change_date")
protected LocalDateTime lastStatusChangeDate; protected Instant lastStatusChangeDate;
/** /**
* The constructor for a database object. * The constructor for a database object.
@ -55,9 +55,9 @@ public class GroupMessage extends Message {
* into a * into a
* database {@link GroupMessage} * database {@link GroupMessage}
* @param lastStatusChangeDate the time stamp to set * @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); super(groupMessage);
memberMessageStatus = groupMessage.getMemberStatuses(); memberMessageStatus = groupMessage.getMemberStatuses();
this.lastStatusChangeDate = lastStatusChangeDate; this.lastStatusChangeDate = lastStatusChangeDate;
@ -92,13 +92,13 @@ public class GroupMessage extends Message {
/** /**
* @return the date at which one of the member statuses changed last * @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 * @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; }
} }

View File

@ -2,7 +2,7 @@ package envoy.server.data;
import static envoy.data.Message.MessageStatus.*; import static envoy.data.Message.MessageStatus.*;
import java.time.LocalDateTime; import java.time.Instant;
import javax.persistence.*; import javax.persistence.*;
@ -12,11 +12,15 @@ import envoy.data.Message.MessageStatus;
import envoy.data.MessageBuilder; import envoy.data.MessageBuilder;
/** /**
* This class serves as a way to let Hibernate communicate with the server * This JPA entity, which will be referred to as database message, stores the
* without bringing the dependency of JPA/Hibernate into the client.<br> * information contained inside a {@link envoy.data.Message} inside the
* It will be referenced as "database message" to clarify between the different * database, while having a slightly different data layout.
* message objects.<br> * <p>
* <br> * 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.
* <p>
* Project: <strong>envoy-server-standalone</strong><br> * Project: <strong>envoy-server-standalone</strong><br>
* File: <strong>Message.java</strong><br> * File: <strong>Message.java</strong><br>
* Created: <strong>02.01.2020</strong><br> * Created: <strong>02.01.2020</strong><br>
@ -29,9 +33,14 @@ import envoy.data.MessageBuilder;
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@NamedQuery( @NamedQuery(
name = Message.getPending, name = Message.getPending,
query = "SELECT m FROM Message m WHERE (m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT) " query = "SELECT m FROM Message m WHERE "
+ "OR (m.sender = :user) AND ((m.status = envoy.data.Message$MessageStatus.RECEIVED) AND (m.receivedDate > :lastSeen)" // Send to or by the user before last seen
+ "OR (m.status = envoy.data.Message$MessageStatus.READ) AND (m.readDate > :lastSeen))" + "(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 { public class Message {
@ -55,13 +64,13 @@ public class Message {
protected Contact recipient; protected Contact recipient;
@Column(name = "creation_date") @Column(name = "creation_date")
protected LocalDateTime creationDate; protected Instant creationDate;
@Column(name = "received_date") @Column(name = "received_date")
protected LocalDateTime receivedDate; protected Instant receivedDate;
@Column(name = "read_date") @Column(name = "read_date")
protected LocalDateTime readDate; protected Instant readDate;
protected String text; protected String text;
protected envoy.data.Message.MessageStatus status; protected envoy.data.Message.MessageStatus status;
@ -107,17 +116,14 @@ public class Message {
* message * message
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public envoy.data.Message toCommon() { public envoy.data.Message toCommon() { return prepareBuilder().build(); }
return prepareBuilder().build();
}
/** /**
* @return a message builder containing the state of this message * @return a message builder containing the state of this message
* @since Envoy Server Standalone v0.1-beta * @since Envoy Server Standalone v0.1-beta
*/ */
MessageBuilder prepareBuilder() { MessageBuilder prepareBuilder() {
var builder = new MessageBuilder(sender.getID(), recipient.getID(), id).setText( var builder = new MessageBuilder(sender.getID(), recipient.getID(), id).setText(text)
text)
.setCreationDate(creationDate) .setCreationDate(creationDate)
.setReceivedDate(receivedDate) .setReceivedDate(receivedDate)
.setReadDate(readDate) .setReadDate(readDate)
@ -134,7 +140,7 @@ public class Message {
* @since Envoy Server Standalone v0.1-beta * @since Envoy Server Standalone v0.1-beta
*/ */
public void received() { public void received() {
receivedDate = LocalDateTime.now(); receivedDate = Instant.now();
status = RECEIVED; status = RECEIVED;
} }
@ -145,7 +151,7 @@ public class Message {
* @since Envoy Server Standalone v0.1-beta * @since Envoy Server Standalone v0.1-beta
*/ */
public void read() { public void read() {
readDate = LocalDateTime.now(); readDate = Instant.now();
status = READ; status = READ;
} }
@ -190,43 +196,43 @@ public class Message {
/** /**
* @return the date at which a {link envoy.data.Message} has been created * @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 * @param creationDate the creation date to set
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.2-beta
* @see Message#getCreationDate() * @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 * @return the date at which a {link envoy.data.Message} has been received by
* the server * 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 * @param receivedDate the received date to set
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.2-beta
* @see Message#getReceivedDate() * @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 * @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 * @param readDate the read date to set
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.2-beta
* @see Message#getReadDate() * @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} * @return the status of a {link envoy.data.Message}

View File

@ -1,6 +1,6 @@
package envoy.server.data; package envoy.server.data;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.List; import java.util.List;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@ -38,7 +38,7 @@ public class PersistenceManager {
.getOnlineUsers() .getOnlineUsers()
.stream() .stream()
.map(this::getUserByID) .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(); transaction.commit();
})); }));
} }
@ -183,15 +183,12 @@ public class PersistenceManager {
* changed. * 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) * @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<Message> getPendingMessages(User user) { public List<Message> getPendingMessages(User user, Instant lastSync) {
return entityManager return entityManager.createNamedQuery(Message.getPending).setParameter("user", user).setParameter("lastSeen", lastSync).getResultList();
.createNamedQuery(Message.getPending)
.setParameter("user", user)
.setParameter("lastSeen", user.getLastSeen())
.getResultList();
} }
/** /**
@ -199,14 +196,15 @@ public class PersistenceManager {
* changed. * 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 * @return all groupMessages that the client does not yet have (unread
* groupMessages) * groupMessages)
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.2-beta
*/ */
public List<GroupMessage> getPendingGroupMessages(User user) { public List<GroupMessage> getPendingGroupMessages(User user, Instant lastSync) {
return entityManager.createNamedQuery(GroupMessage.getPendingGroupMsg) return entityManager.createNamedQuery(GroupMessage.getPendingGroupMsg)
.setParameter("userId", user.getID()) .setParameter("userId", user.getID())
.setParameter("lastSeen", user.getLastSeen()) .setParameter("lastSeen", lastSync)
.getResultList(); .getResultList();
} }
@ -221,8 +219,7 @@ public class PersistenceManager {
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public List<User> searchUsers(String searchPhrase, long userId) { public List<User> searchUsers(String searchPhrase, long userId) {
return entityManager.createNamedQuery( return entityManager.createNamedQuery(User.searchByName)
User.searchByName)
.setParameter("searchPhrase", searchPhrase + "%") .setParameter("searchPhrase", searchPhrase + "%")
.setParameter("context", getUserByID(userId)) .setParameter("context", getUserByID(userId))
.getResultList(); .getResultList();

View File

@ -1,6 +1,6 @@
package envoy.server.data; package envoy.server.data;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -66,7 +66,7 @@ public class User extends Contact {
private String passwordHash; private String passwordHash;
@Column(name = "last_seen") @Column(name = "last_seen")
private LocalDateTime lastSeen; private Instant lastSeen;
private UserStatus status; private UserStatus status;
@ -92,15 +92,15 @@ public class User extends Contact {
/** /**
* @return the last date the user has been online * @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 * @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 * @return the status

View File

@ -1,6 +1,6 @@
package envoy.server.net; package envoy.server.net;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -52,7 +52,7 @@ public class ConnectionManager implements ISocketIdListener {
// Notify contacts of this users offline-going // Notify contacts of this users offline-going
envoy.server.data.User user = PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID)); envoy.server.data.User user = PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
user.setStatus(UserStatus.OFFLINE); user.setStatus(UserStatus.OFFLINE);
user.setLastSeen(LocalDateTime.now()); user.setLastSeen(Instant.now());
UserStatusChangeProcessor.updateUserStatus(user); UserStatusChangeProcessor.updateUserStatus(user);
// Remove the socket // Remove the socket

View File

@ -2,7 +2,7 @@ package envoy.server.processors;
import static envoy.data.Message.MessageStatus.*; import static envoy.data.Message.MessageStatus.*;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -56,7 +56,7 @@ public class GroupMessageProcessor implements ObjectProcessor<GroupMessage> {
groupMessage); groupMessage);
try { 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) { } catch (EntityExistsException e) {
logger.warning("Received a groupMessage with an ID that already exists"); logger.warning("Received a groupMessage with an ID that already exists");
} }

View File

@ -2,7 +2,7 @@ package envoy.server.processors;
import static envoy.data.Message.MessageStatus.READ; import static envoy.data.Message.MessageStatus.READ;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -42,7 +42,7 @@ public class GroupMessageStatusChangeProcessor implements ObjectProcessor<GroupM
// Apply the status change // Apply the status change
gmsg.getMemberMessageStatus().replace(statusChange.getMemberID(), statusChange.get()); gmsg.getMemberMessageStatus().replace(statusChange.getMemberID(), statusChange.get());
gmsg.setLastStatusChangeDate(LocalDateTime.now()); gmsg.setLastStatusChangeDate(Instant.now());
// Notifying the other members about the status change // Notifying the other members about the status change
final var userID = connectionManager.getUserIDBySocketID(socketID); final var userID = connectionManager.getUserIDBySocketID(socketID);
@ -59,7 +59,7 @@ public class GroupMessageStatusChangeProcessor implements ObjectProcessor<GroupM
// Notify online members about the status change // Notify online members about the status change
writeProxy.writeToOnlineContacts(gmsg.getRecipient().getContacts(), writeProxy.writeToOnlineContacts(gmsg.getRecipient().getContacts(),
new MessageStatusChange(gmsg.getID(), gmsg.getStatus(), LocalDateTime.now())); new MessageStatusChange(gmsg.getID(), gmsg.getStatus(), Instant.now()));
} }
persistenceManager.updateMessage(gmsg); persistenceManager.updateMessage(gmsg);
} }

View File

@ -4,7 +4,7 @@ import static envoy.data.Message.MessageStatus.*;
import static envoy.data.User.UserStatus.ONLINE; import static envoy.data.User.UserStatus.ONLINE;
import static envoy.event.HandshakeRejection.*; import static envoy.event.HandshakeRejection.*;
import java.time.LocalDateTime; import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -47,7 +47,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
@Override @Override
public void process(LoginCredentials credentials, long socketID, ObjectWriteProxy writeProxy) { public void process(LoginCredentials credentials, long socketID, ObjectWriteProxy writeProxy) {
// Cache this write proxy for user-independant notifications // Cache this write proxy for user-independent notifications
UserStatusChangeProcessor.setWriteProxy(writeProxy); UserStatusChangeProcessor.setWriteProxy(writeProxy);
if (!VersionUtil.verifyCompatibility(credentials.getClientVersion())) { if (!VersionUtil.verifyCompatibility(credentials.getClientVersion())) {
@ -98,7 +98,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
// Creation of a new user // Creation of a new user
user = new User(); user = new User();
user.setName(credentials.getIdentifier()); user.setName(credentials.getIdentifier());
user.setLastSeen(LocalDateTime.now()); user.setLastSeen(Instant.now());
user.setStatus(ONLINE); user.setStatus(ONLINE);
user.setPasswordHash(PasswordUtil.hash(credentials.getPassword())); user.setPasswordHash(PasswordUtil.hash(credentials.getPassword()));
user.setContacts(new HashSet<>()); user.setContacts(new HashSet<>());
@ -114,16 +114,17 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
user.setStatus(ONLINE); user.setStatus(ONLINE);
UserStatusChangeProcessor.updateUserStatus(user); UserStatusChangeProcessor.updateUserStatus(user);
// Complete the handshake final var pendingMessages = PersistenceManager.getInstance().getPendingMessages(user, credentials.getLastSync());
writeProxy.write(socketID, user.toCommon());
final var pendingMessages = PersistenceManager.getInstance().getPendingMessages(user);
pendingMessages.removeIf(GroupMessage.class::isInstance); pendingMessages.removeIf(GroupMessage.class::isInstance);
logger.fine("Sending " + pendingMessages.size() + " pending messages to " + user + "..."); logger.fine("Sending " + pendingMessages.size() + " pending messages to " + user + "...");
for (var msg : pendingMessages) { for (var msg : pendingMessages) {
final var msgCommon = msg.toCommon(); final var msgCommon = msg.toCommon();
if (msg.getStatus() == SENT) { if (msg.getCreationDate().isAfter(credentials.getLastSync())) {
// Sync without side effects
writeProxy.write(socketID, msgCommon);
} else if (msg.getStatus() == SENT) {
// Send the message // Send the message
writeProxy.write(socketID, msgCommon); writeProxy.write(socketID, msgCommon);
@ -138,42 +139,46 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
} else writeProxy.write(socketID, new MessageStatusChange(msgCommon)); } else writeProxy.write(socketID, new MessageStatusChange(msgCommon));
} }
List<GroupMessage> pendingGroupMessages = PersistenceManager.getInstance().getPendingGroupMessages(user); List<GroupMessage> pendingGroupMessages = PersistenceManager.getInstance().getPendingGroupMessages(user, credentials.getLastSync());
logger.fine("Sending " + pendingGroupMessages.size() + " pending group messages to " + user + "..."); logger.fine("Sending " + pendingGroupMessages.size() + " pending group messages to " + user + "...");
for (var gmsg : pendingGroupMessages) { for (var gmsg : pendingGroupMessages) {
final var gmsgCommon = gmsg.toCommon(); final var gmsgCommon = gmsg.toCommon();
// Deliver the message to the user if he hasn't received it yet // Deliver the message to the user if he hasn't received it yet
if (gmsg.getMemberMessageStatus().get(user.getID()) == SENT) { if (gmsg.getCreationDate().isAfter(credentials.getLastSync()) || gmsg.getMemberMessageStatus().get(user.getID()) == SENT) {
gmsg.getMemberMessageStatus().replace(user.getID(), RECEIVED); if (gmsg.getMemberMessageStatus().replace(user.getID(), RECEIVED) != RECEIVED) {
gmsg.setLastStatusChangeDate(LocalDateTime.now()); gmsg.setLastStatusChangeDate(Instant.now());
writeProxy.write(socketID, gmsgCommon); writeProxy.write(socketID, gmsgCommon);
// Notify all online group members about the status change // Notify all online group members about the status change
writeProxy.writeToOnlineContacts(gmsg.getRecipient().getContacts(), writeProxy.writeToOnlineContacts(gmsg.getRecipient().getContacts(),
new GroupMessageStatusChange(gmsg.getID(), RECEIVED, LocalDateTime new GroupMessageStatusChange(gmsg.getID(), RECEIVED, Instant.now(), connectionManager.getUserIDBySocketID(socketID)));
.now(),
connectionManager.getUserIDBySocketID(socketID)));
if (Collections.min(gmsg.getMemberMessageStatus().values()) == RECEIVED) { if (Collections.min(gmsg.getMemberMessageStatus().values()) == RECEIVED) {
gmsg.received(); gmsg.received();
// Notify online members about the status change // Notify online members about the status change
writeProxy.writeToOnlineContacts(gmsg.getRecipient().getContacts(), writeProxy.writeToOnlineContacts(gmsg.getRecipient().getContacts(),
new MessageStatusChange(gmsg.getID(), gmsg.getStatus(), LocalDateTime.now())); new MessageStatusChange(gmsg.getID(), gmsg.getStatus(), Instant.now()));
} }
PersistenceManager.getInstance().updateMessage(gmsg); PersistenceManager.getInstance().updateMessage(gmsg);
} else { } else {
// Just send the message without updating if it was received in the past
writeProxy.write(socketID, gmsgCommon);
}
} else {
// Sending group message status changes // Sending group message status changes
if (gmsg.getStatus() == SENT && gmsg.getLastStatusChangeDate().isAfter(gmsg.getCreationDate()) if (gmsg.getStatus() == SENT && gmsg.getLastStatusChangeDate().isAfter(gmsg.getCreationDate())
|| gmsg.getStatus() == RECEIVED && gmsg.getLastStatusChangeDate().isAfter(gmsg.getReceivedDate())) { || gmsg.getStatus() == RECEIVED && gmsg.getLastStatusChangeDate().isAfter(gmsg.getReceivedDate())) {
gmsg.getMemberMessageStatus().forEach((memberID, memberStatus) -> gmsg.getMemberMessageStatus()
writeProxy.write(socketID, new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID))); .forEach((memberID, memberStatus) -> writeProxy.write(socketID,
new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID)));
} }
// Deliver just a status change instead of the whole message // Deliver just a status change instead of the whole message
@ -182,6 +187,9 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
writeProxy.write(socketID, new MessageStatusChange(gmsgCommon)); writeProxy.write(socketID, new MessageStatusChange(gmsgCommon));
} }
} }
// Complete the handshake
writeProxy.write(socketID, user.toCommon());
} }
@Override @Override