From cdef310a69a41a8ffe3524b371218f3cf25434d2 Mon Sep 17 00:00:00 2001 From: kske Date: Thu, 6 Feb 2020 18:35:05 +0100 Subject: [PATCH 1/7] Replaced MessageCache by Cache This class will be used to implement the offline cache for both messages and events. --- src/main/java/envoy/client/data/Cache.java | 58 +++++++++++++++++++ src/main/java/envoy/client/net/Client.java | 5 +- .../java/envoy/client/net/MessageCache.java | 54 ----------------- src/main/java/envoy/client/ui/Startup.java | 10 ++-- 4 files changed, 65 insertions(+), 62 deletions(-) create mode 100644 src/main/java/envoy/client/data/Cache.java delete mode 100644 src/main/java/envoy/client/net/MessageCache.java diff --git a/src/main/java/envoy/client/data/Cache.java b/src/main/java/envoy/client/data/Cache.java new file mode 100644 index 0000000..366f703 --- /dev/null +++ b/src/main/java/envoy/client/data/Cache.java @@ -0,0 +1,58 @@ +package envoy.client.data; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import envoy.client.util.EnvoyLog; + +/** + * Stores elements in a queue to process them later.
+ *
+ * Project: envoy-client
+ * File: Cache.java
+ * Created: 6 Feb 2020
+ * + * @param the type of cached elements + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class Cache implements Consumer { + + private final Queue elements = new LinkedList<>(); + private Consumer processor; + + private static final Logger logger = EnvoyLog.getLogger(Cache.class.getSimpleName()); + + /** + * Adds an element to the cache. + * + * @param element the element to add + * @since Envoy v0.3-alpha + */ + @Override + public void accept(T element) { + logger.info(String.format("Adding element %s to cache", element)); + elements.offer(element); + } + + /** + * Sets the processor to which cached elements are relayed. + * + * @param processor the processor to set + * @since Envoy v0.3-alpha + */ + public void setProcessor(Consumer processor) { this.processor = processor; } + + /** + * Relays all cached elements to the processor. + * + * @throws IllegalStateException if the processor is not initialized + * @since Envoy v0.3-alpha + */ + public void relay() { + if (processor == null) throw new IllegalStateException("Processor is not defined"); + elements.forEach(processor::accept); + } +} diff --git a/src/main/java/envoy/client/net/Client.java b/src/main/java/envoy/client/net/Client.java index 91e9040..2e96b4a 100644 --- a/src/main/java/envoy/client/net/Client.java +++ b/src/main/java/envoy/client/net/Client.java @@ -10,6 +10,7 @@ import java.util.logging.Logger; import javax.naming.TimeLimitExceededException; import envoy.client.Config; +import envoy.client.data.Cache; import envoy.client.data.LocalDb; import envoy.client.util.EnvoyLog; import envoy.data.*; @@ -61,7 +62,7 @@ public class Client implements Closeable { * failed for some other reason * @since Envoy v0.2-alpha */ - public MessageCache onlineInit(LoginCredentials credentials, LocalDb localDb) throws Exception { + public Cache onlineInit(LoginCredentials credentials, LocalDb localDb) throws Exception { // Establish TCP connection logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort())); socket = new Socket(config.getServer(), config.getPort()); @@ -71,7 +72,7 @@ public class Client implements Closeable { receiver = new Receiver(socket.getInputStream()); // Create cache for unread messages - final MessageCache cache = new MessageCache(); + final Cache cache = new Cache<>(); // Register user creation processor, contact list processor and message cache receiver.registerProcessor(User.class, sender -> { logger.info("Acquired user object " + sender); this.sender = sender; }); diff --git a/src/main/java/envoy/client/net/MessageCache.java b/src/main/java/envoy/client/net/MessageCache.java deleted file mode 100644 index 1fc49c3..0000000 --- a/src/main/java/envoy/client/net/MessageCache.java +++ /dev/null @@ -1,54 +0,0 @@ -package envoy.client.net; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.function.Consumer; -import java.util.logging.Logger; - -import envoy.client.util.EnvoyLog; -import envoy.data.Message; - -/** - * Stores messages in a queue until the application initialization is complete. - * The messages can then be relayed to a processor.
- *
- * Project: envoy-client
- * File: MessageCache.java
- * Created: 4 Feb 2020
- * - * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha - */ -public class MessageCache implements Consumer { - - private final Queue messages = new LinkedList<>(); - private Consumer processor; - - private static final Logger logger = EnvoyLog.getLogger(MessageCache.class.getSimpleName()); - - /** - * Adds a message to the cache. - * - * @since Envoy v0.3-alpha - */ - @Override - public void accept(Message message) { - logger.info(String.format("Adding message %s to cache", message)); - messages.add(message); - } - - /** - * Sets the processor to which messages are relayed. - * - * @param processor the processor to set - * @since Envoy v0.3-alpha - */ - public void setProcessor(Consumer processor) { this.processor = processor; } - - /** - * Relays all cached messages to the processor. - * - * @since Envoy v0.3-alpha - */ - public void relayMessages() { messages.forEach(processor::accept); } -} diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 9f308dc..1a38da9 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -13,13 +13,11 @@ import javax.swing.SwingUtilities; import envoy.client.Config; import envoy.client.Settings; -import envoy.client.data.LocalDb; -import envoy.client.data.PersistentLocalDb; -import envoy.client.data.TransientLocalDb; +import envoy.client.data.*; import envoy.client.net.Client; -import envoy.client.net.MessageCache; import envoy.client.util.EnvoyLog; import envoy.data.LoginCredentials; +import envoy.data.Message; import envoy.data.User; import envoy.exception.EnvoyException; @@ -108,7 +106,7 @@ public class Startup { // Acquire the client user (with ID) either from the server or from the local // database, which triggers offline mode Client client = new Client(); - MessageCache cache = null; + Cache cache = null; try { // Try entering online mode first localDb.loadIdGenerator(); @@ -178,7 +176,7 @@ public class Startup { }); // Relay unread messages from cache - if (cache != null) cache.relayMessages(); + if (cache != null) cache.relay(); // Save Settings and PersistentLocalDb on shutdown Runtime.getRuntime().addShutdownHook(new Thread(() -> { From bf38d2f19f589dad729c0a20fde0878833012ba1 Mon Sep 17 00:00:00 2001 From: kske Date: Thu, 6 Feb 2020 21:03:08 +0100 Subject: [PATCH 2/7] Added WriteProxy with creation method in Client, added caches to LocalDb --- src/main/java/envoy/client/data/Cache.java | 5 +- src/main/java/envoy/client/data/LocalDb.java | 36 ++++++- .../envoy/client/data/PersistentLocalDb.java | 14 +-- src/main/java/envoy/client/net/Client.java | 11 ++ .../java/envoy/client/net/WriteProxy.java | 102 ++++++++++++++++++ 5 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 src/main/java/envoy/client/net/WriteProxy.java diff --git a/src/main/java/envoy/client/data/Cache.java b/src/main/java/envoy/client/data/Cache.java index 366f703..458cb0a 100644 --- a/src/main/java/envoy/client/data/Cache.java +++ b/src/main/java/envoy/client/data/Cache.java @@ -20,8 +20,8 @@ import envoy.client.util.EnvoyLog; */ public class Cache implements Consumer { - private final Queue elements = new LinkedList<>(); - private Consumer processor; + private final Queue elements = new LinkedList<>(); + private transient Consumer processor; private static final Logger logger = EnvoyLog.getLogger(Cache.class.getSimpleName()); @@ -54,5 +54,6 @@ public class Cache implements Consumer { public void relay() { if (processor == null) throw new IllegalStateException("Processor is not defined"); elements.forEach(processor::accept); + elements.clear(); } } diff --git a/src/main/java/envoy/client/data/LocalDb.java b/src/main/java/envoy/client/data/LocalDb.java index 79368a8..a606856 100644 --- a/src/main/java/envoy/client/data/LocalDb.java +++ b/src/main/java/envoy/client/data/LocalDb.java @@ -3,7 +3,9 @@ package envoy.client.data; import java.util.*; import envoy.data.IdGenerator; +import envoy.data.Message; import envoy.data.User; +import envoy.event.MessageStatusChangeEvent; /** * Stores information about the current {@link User} and their {@link Chat}s. @@ -18,10 +20,12 @@ import envoy.data.User; */ public abstract class LocalDb { - protected User user; - protected Map users = new HashMap<>(); - protected List chats = new ArrayList<>(); - protected IdGenerator idGenerator; + protected User user; + protected Map users = new HashMap<>(); + protected List chats = new ArrayList<>(); + protected IdGenerator idGenerator; + protected Cache messageCache; + protected Cache statusCache; /** * Initializes a storage space for a user-specific list of chats. @@ -115,4 +119,28 @@ public abstract class LocalDb { * @since Envoy v0.3-alpha */ public boolean hasIdGenerator() { return idGenerator != null; } + + /** + * @return the offline message cache + * @since Envoy v0.3-alpha + */ + public Cache getMessageCache() { return messageCache; } + + /** + * @param messageCache the offline message cache to set + * @since Envoy v0.3-alpha + */ + public void setMessageCache(Cache messageCache) { this.messageCache = messageCache; } + + /** + * @return the offline status cache + * @since Envoy v0.3-alpha + */ + public Cache getStatusCache() { return statusCache; } + + /** + * @param statusCache the offline status cache to set + * @since Envoy v0.3-alpha + */ + public void setStatusCache(Cache statusCache) { this.statusCache = statusCache; } } diff --git a/src/main/java/envoy/client/data/PersistentLocalDb.java b/src/main/java/envoy/client/data/PersistentLocalDb.java index 7856517..7052c60 100644 --- a/src/main/java/envoy/client/data/PersistentLocalDb.java +++ b/src/main/java/envoy/client/data/PersistentLocalDb.java @@ -40,18 +40,18 @@ public class PersistentLocalDb extends LocalDb { * Constructs an empty local database. To serialize any chats to the file * system, call {@link PersistentLocalDb#initializeUserStorage()}. * - * @param localDBDir the directory in which to store users and chats + * @param localDbDir the directory in which to store users and chats * @throws IOException if the PersistentLocalDb could not be initialized * @since Envoy v0.1-alpha */ - public PersistentLocalDb(File localDBDir) throws IOException { - this.localDBDir = localDBDir; + public PersistentLocalDb(File localDbDir) throws IOException { + localDBDir = localDbDir; // Initialize local database directory - if (localDBDir.exists() && !localDBDir.isDirectory()) - throw new IOException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); - usersFile = new File(localDBDir, "users.db"); - idGeneratorFile = new File(localDBDir, "id_generator.db"); + if (localDbDir.exists() && !localDbDir.isDirectory()) + throw new IOException(String.format("LocalDbDir '%s' is not a directory!", localDbDir.getAbsolutePath())); + usersFile = new File(localDbDir, "users.db"); + idGeneratorFile = new File(localDbDir, "id_generator.db"); } /** diff --git a/src/main/java/envoy/client/net/Client.java b/src/main/java/envoy/client/net/Client.java index 2e96b4a..29b2f03 100644 --- a/src/main/java/envoy/client/net/Client.java +++ b/src/main/java/envoy/client/net/Client.java @@ -118,6 +118,17 @@ public class Client implements Closeable { return cache; } + /** + * Creates a new write proxy that uses this client to communicate with the + * server. + * + * @param localDb the local database that the write proxy will use to access + * caches + * @return a new write proxy + * @since Envoy Client v0.3-alpha + */ + public WriteProxy createWriteProxy(LocalDb localDb) { return new WriteProxy(this, localDb); } + /** * Sends a message to the server. The message's status will be incremented once * it was delivered successfully. diff --git a/src/main/java/envoy/client/net/WriteProxy.java b/src/main/java/envoy/client/net/WriteProxy.java new file mode 100644 index 0000000..454751f --- /dev/null +++ b/src/main/java/envoy/client/net/WriteProxy.java @@ -0,0 +1,102 @@ +package envoy.client.net; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import envoy.client.data.LocalDb; +import envoy.client.util.EnvoyLog; +import envoy.data.Message; +import envoy.event.MessageStatusChangeEvent; + +/** + * Implements methods to send {@link Message}s and + * {@link MessageStatusChangeEvent}s to the server or cache them inside a + * {@link LocalDb} depending on the online status.
+ *
+ * Project: envoy-client
+ * File: WriteProxy.java
+ * Created: 6 Feb 2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class WriteProxy { + + private final Client client; + private final LocalDb localDb; + + private static final Logger logger = EnvoyLog.getLogger(WriteProxy.class.getSimpleName()); + + /** + * Initializes a write proxy using a client and a local database. The + * corresponding cache processors are injected into the caches. + * + * @param client the client used to send messages and message status change + * events + * @param localDb the local database used to cache messages and message status + * change events + * @since Envoy v0.3-alpha + */ + public WriteProxy(Client client, LocalDb localDb) { + this.client = client; + this.localDb = localDb; + + // Initialize cache processors for messages and message status change events + localDb.getMessageCache().setProcessor(msg -> { + try { + client.sendMessage(msg); + } catch (IOException e) { + logger.log(Level.SEVERE, "Could not send cached message", e); + } + }); + localDb.getStatusCache().setProcessor(evt -> { + try { + client.sendEvent(evt); + } catch (IOException e) { + logger.log(Level.SEVERE, "Could not send cached message status change event", e); + } + }); + } + + /** + * Sends cached {@link Message}s and {@link MessageStatusChangeEvent}s to the + * server. + * + * @since Envoy v0.3-alpha + */ + public void flushCache() { + + // Send messages + localDb.getMessageCache().relay(); + + // Send message status change events + localDb.getStatusCache().relay(); + } + + /** + * Delivers a message to the server if online. Otherwise the message is cached + * inside the local database. + * + * @param message the message to send + * @throws IOException if the message could not be sent + * @since Envoy v0.3-alpha + */ + public void writeMessage(Message message) throws IOException { + if (client.isOnline()) client.sendMessage(message); + else localDb.getMessageCache().accept(message); + } + + /** + * Delivers a message status change event to the server if online. Otherwise the + * event is cached inside the local database. + * + * @param evt the event to send + * @throws IOException if the event could not be sent + * @since Envoy v0.3-alpha + */ + public void writeMessageStatusChangeEvent(MessageStatusChangeEvent evt) throws IOException { + if (client.isOnline()) client.sendEvent(evt); + else localDb.getStatusCache().accept(evt); + } +} From 4afe073e79d232ef79641085f61e3085c597722b Mon Sep 17 00:00:00 2001 From: kske Date: Thu, 6 Feb 2020 21:28:02 +0100 Subject: [PATCH 3/7] Integrated WriteProxy into the sending process --- src/main/java/envoy/client/data/Chat.java | 12 +++-- src/main/java/envoy/client/ui/ChatWindow.java | 44 +++++++++---------- src/main/java/envoy/client/ui/Startup.java | 14 ++++-- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/main/java/envoy/client/data/Chat.java b/src/main/java/envoy/client/data/Chat.java index f10e816..49083a7 100644 --- a/src/main/java/envoy/client/data/Chat.java +++ b/src/main/java/envoy/client/data/Chat.java @@ -3,7 +3,7 @@ package envoy.client.data; import java.io.IOException; import java.io.Serializable; -import envoy.client.net.Client; +import envoy.client.net.WriteProxy; import envoy.client.ui.list.ComponentListModel; import envoy.data.Message; import envoy.data.Message.MessageStatus; @@ -52,22 +52,20 @@ public class Chat implements Serializable { * {@code READ} starting from the bottom and stopping once a read message is * found. * - * @param client the client instance used to notify the server about the message - * status changes + * @param writeProxy the write proxy instance used to notify the server about + * the message status changes * @throws IOException if a {@link MessageStatusChangeEvent} could not be * delivered to the server * @since Envoy v0.3-alpha */ - public void read(Client client) throws IOException { + public void read(WriteProxy writeProxy) throws IOException { for (int i = model.size() - 1; i >= 0; --i) { final Message m = model.get(i); if (m.getSenderId() == recipient.getId()) { if (m.getStatus() == MessageStatus.READ) break; else { m.setStatus(MessageStatus.READ); - - // TODO: Cache events in offline mode - client.sendEvent(new MessageStatusChangeEvent(m)); + writeProxy.writeMessageStatusChangeEvent(new MessageStatusChangeEvent(m)); } } } diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 3abd54a..7d537bf 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -16,6 +16,7 @@ import envoy.client.data.LocalDb; import envoy.client.event.MessageCreationEvent; import envoy.client.event.ThemeChangeEvent; import envoy.client.net.Client; +import envoy.client.net.WriteProxy; import envoy.client.ui.list.ComponentList; import envoy.client.ui.settings.SettingsScreen; import envoy.client.util.EnvoyLog; @@ -39,8 +40,9 @@ import envoy.event.MessageStatusChangeEvent; public class ChatWindow extends JFrame { // User specific objects - private Client client; - private LocalDb localDb; + private Client client; + private WriteProxy writeProxy; + private LocalDb localDb; // GUI components private JPanel contentPane = new JPanel(); @@ -211,7 +213,7 @@ public class ChatWindow extends JFrame { // Listen to received messages EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { - Message message = ((MessageCreationEvent) evt).get(); + Message message = ((MessageCreationEvent) evt).get(); Chat chat = localDb.getChats().stream().filter(c -> c.getRecipient().getId() == message.getSenderId()).findFirst().get(); chat.appendMessage(message); @@ -301,8 +303,7 @@ public class ChatWindow extends JFrame { .build(); // Send message - // TODO: Store offline messages - client.sendMessage(message); + writeProxy.writeMessage(message); // Add message to PersistentLocalDb and update UI currentChat.appendMessage(message); @@ -345,7 +346,7 @@ public class ChatWindow extends JFrame { private void readCurrentChat() { try { - currentChat.read(client); + currentChat.read(writeProxy); messageList.synchronizeModel(); } catch (IOException e) { e.printStackTrace(); @@ -354,24 +355,23 @@ public class ChatWindow extends JFrame { } /** - * Sets the {@link Client} used by this {@link ChatWindow}. + * Initializes the components responsible server communication and + * persistence.
+ *
+ * This will trigger the display of the contact list. * - * @param client the {@link Client} used to send and receive messages - * @since Envoy v0.2-alpha + * @param client the client used to send and receive messages + * @param localDb the local database used to manage stored messages + * and users + * @param writeProxy the write proxy used to send messages and status change + * events to the server or cache them inside the local + * database + * @since Envoy v0.3-alpha */ - public void setClient(Client client) { this.client = client; } - - /** - * Sets the {@link LocalDb} used by this {@link ChatWindow}. After - * invoking this - * method, users and chats will be loaded from the database into the GUI. - * - * @param localDb the {@link LocalDb} used to manage stored messages - * and users - * @since Envoy v0.2-alpha - */ - public void setLocalDB(LocalDb localDb) { - this.localDb = localDb; + public void initContent(Client client, LocalDb localDb, WriteProxy writeProxy) { + this.client = client; + this.localDb = localDb; + this.writeProxy = writeProxy; loadUsersAndChats(); } } diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 1a38da9..1c68e41 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -15,6 +15,7 @@ import envoy.client.Config; import envoy.client.Settings; import envoy.client.data.*; import envoy.client.net.Client; +import envoy.client.net.WriteProxy; import envoy.client.util.EnvoyLog; import envoy.data.LoginCredentials; import envoy.data.Message; @@ -148,14 +149,19 @@ public class Startup { JOptionPane.WARNING_MESSAGE); } - // Save all users to the local database - if (client.isOnline()) localDb.setUsers(client.getUsers()); + // Initialize write proxy + final WriteProxy writeProxy = client.createWriteProxy(localDb); + + // Save all users to the local database and flush cache + if (client.isOnline()) { + localDb.setUsers(client.getUsers()); + writeProxy.flushCache(); + } // Display ChatWindow and StatusTrayIcon EventQueue.invokeLater(() -> { try { - chatWindow.setClient(client); - chatWindow.setLocalDB(localDb); + chatWindow.initContent(client, localDb, writeProxy); try { new StatusTrayIcon(chatWindow).show(); From 74715bbf8214f7fff2cdf30931b02ce7e4ccc821 Mon Sep 17 00:00:00 2001 From: kske Date: Thu, 6 Feb 2020 21:42:17 +0100 Subject: [PATCH 4/7] Persisting cache in PersistentLocalDb --- src/main/java/envoy/client/data/Cache.java | 4 +++- src/main/java/envoy/client/data/LocalDb.java | 8 ++++---- .../envoy/client/data/PersistentLocalDb.java | 20 ++++++++++++++----- src/main/java/envoy/client/ui/Startup.java | 2 +- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/main/java/envoy/client/data/Cache.java b/src/main/java/envoy/client/data/Cache.java index 458cb0a..80ef26f 100644 --- a/src/main/java/envoy/client/data/Cache.java +++ b/src/main/java/envoy/client/data/Cache.java @@ -1,5 +1,6 @@ package envoy.client.data; +import java.io.Serializable; import java.util.LinkedList; import java.util.Queue; import java.util.function.Consumer; @@ -18,12 +19,13 @@ import envoy.client.util.EnvoyLog; * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ -public class Cache implements Consumer { +public class Cache implements Consumer, Serializable { private final Queue elements = new LinkedList<>(); private transient Consumer processor; private static final Logger logger = EnvoyLog.getLogger(Cache.class.getSimpleName()); + private static final long serialVersionUID = 7343544675545545076L; /** * Adds an element to the cache. diff --git a/src/main/java/envoy/client/data/LocalDb.java b/src/main/java/envoy/client/data/LocalDb.java index a606856..8034e2a 100644 --- a/src/main/java/envoy/client/data/LocalDb.java +++ b/src/main/java/envoy/client/data/LocalDb.java @@ -24,8 +24,8 @@ public abstract class LocalDb { protected Map users = new HashMap<>(); protected List chats = new ArrayList<>(); protected IdGenerator idGenerator; - protected Cache messageCache; - protected Cache statusCache; + protected Cache messageCache = new Cache<>(); + protected Cache statusCache = new Cache<>(); /** * Initializes a storage space for a user-specific list of chats. @@ -52,12 +52,12 @@ public abstract class LocalDb { public void loadUsers() throws Exception {} /** - * Loads all chat data of the client user. + * Loads all data of the client user. * * @throws Exception if the loading process failed * @since Envoy v0.3-alpha */ - public void loadChats() throws Exception {} + public void loadUserData() throws Exception {} /** * Loads the ID generator. Any exception thrown during this process is ignored. diff --git a/src/main/java/envoy/client/data/PersistentLocalDb.java b/src/main/java/envoy/client/data/PersistentLocalDb.java index 7052c60..d6e594a 100644 --- a/src/main/java/envoy/client/data/PersistentLocalDb.java +++ b/src/main/java/envoy/client/data/PersistentLocalDb.java @@ -23,7 +23,7 @@ import envoy.util.SerializationUtils; */ public class PersistentLocalDb extends LocalDb { - private File localDBDir, localDBFile, usersFile, idGeneratorFile; + private File localDBDir, localDBFile, usersFile, idGeneratorFile, messageCacheFile, statusCacheFile; /** * Initializes an empty local database without a directory. All changes made to @@ -63,7 +63,9 @@ public class PersistentLocalDb extends LocalDb { @Override public void initializeUserStorage() { if (user == null) throw new NullPointerException("Client user is null"); - localDBFile = new File(localDBDir, user.getId() + ".db"); + localDBFile = new File(localDBDir, user.getId() + ".db"); + messageCacheFile = new File(localDBDir, user.getId() + "_message_cache.db"); + statusCacheFile = new File(localDBDir, user.getId() + "_status_cache.db"); } @Override @@ -71,8 +73,12 @@ public class PersistentLocalDb extends LocalDb { // Save users SerializationUtils.write(usersFile, users); - // Save chats - if (user != null) SerializationUtils.write(localDBFile, chats); + // Save user data + if (user != null) { + SerializationUtils.write(localDBFile, chats); + SerializationUtils.write(messageCacheFile, messageCache); + SerializationUtils.write(statusCacheFile, statusCache); + } // Save id generator if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); @@ -82,7 +88,11 @@ public class PersistentLocalDb extends LocalDb { public void loadUsers() throws ClassNotFoundException, IOException { users = SerializationUtils.read(usersFile, HashMap.class); } @Override - public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); } + public void loadUserData() throws ClassNotFoundException, IOException { + chats = SerializationUtils.read(localDBFile, ArrayList.class); + messageCache = SerializationUtils.read(messageCacheFile, Cache.class); + statusCache = SerializationUtils.read(statusCacheFile, Cache.class); + } @Override public void loadIdGenerator() { diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 1c68e41..46f7951 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -138,7 +138,7 @@ public class Startup { // Initialize chats in local database try { localDb.initializeUserStorage(); - localDb.loadChats(); + localDb.loadUserData(); } catch (FileNotFoundException e) { // The local database file has not yet been created, probably first login } catch (Exception e) { From effe41f4585cea8f3d8bd8b9dd1b86ea86212a15 Mon Sep 17 00:00:00 2001 From: kske Date: Thu, 6 Feb 2020 22:17:14 +0100 Subject: [PATCH 5/7] Implemented login through command line arguments * Moved Config and ConfigItem to data package * Added mandatory property to ConfigItem * Added user and password ConfigItems to Config --- src/main/java/envoy/client/Settings.java | 1 + .../java/envoy/client/{ => data}/Config.java | 59 +++++++++++++++---- .../envoy/client/{ => data}/ConfigItem.java | 46 ++++++++++++--- .../envoy/client/data/PersistentLocalDb.java | 1 - src/main/java/envoy/client/net/Client.java | 2 +- src/main/java/envoy/client/ui/Startup.java | 7 +-- src/main/java/envoy/client/util/EnvoyLog.java | 2 +- 7 files changed, 91 insertions(+), 27 deletions(-) rename src/main/java/envoy/client/{ => data}/Config.java (69%) rename src/main/java/envoy/client/{ => data}/ConfigItem.java (56%) diff --git a/src/main/java/envoy/client/Settings.java b/src/main/java/envoy/client/Settings.java index 02b6c88..efb03d6 100644 --- a/src/main/java/envoy/client/Settings.java +++ b/src/main/java/envoy/client/Settings.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.Map; import java.util.prefs.Preferences; +import envoy.client.data.Config; import envoy.client.ui.Color; import envoy.client.ui.Theme; import envoy.util.SerializationUtils; diff --git a/src/main/java/envoy/client/Config.java b/src/main/java/envoy/client/data/Config.java similarity index 69% rename from src/main/java/envoy/client/Config.java rename to src/main/java/envoy/client/data/Config.java index 24e7780..1f351ea 100644 --- a/src/main/java/envoy/client/Config.java +++ b/src/main/java/envoy/client/data/Config.java @@ -1,9 +1,12 @@ -package envoy.client; +package envoy.client.data; import java.io.File; +import java.security.NoSuchAlgorithmException; import java.util.*; +import java.util.function.Function; import java.util.logging.Level; +import envoy.data.LoginCredentials; import envoy.exception.EnvoyException; /** @@ -26,14 +29,15 @@ public class Config { private static Config config; private Config() { - items.put("server", new ConfigItem<>("server", "s", (input) -> input, null)); - items.put("port", new ConfigItem<>("port", "p", (input) -> Integer.parseInt(input), null)); - items.put("localDB", new ConfigItem<>("localDB", "db", (input) -> new File(input), new File("localDB"))); - items.put("ignoreLocalDB", new ConfigItem<>("ignoreLocalDB", "nodb", (input) -> Boolean.parseBoolean(input), false)); - items.put("homeDirectory", - new ConfigItem<>("homeDirectory", "h", (input) -> new File(input), new File(System.getProperty("user.home"), ".envoy"))); - items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", (input) -> Level.parse(input), Level.CONFIG)); - items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", (input) -> Level.parse(input), Level.FINEST)); + items.put("server", new ConfigItem<>("server", "s", Function.identity(), null, true)); + items.put("port", new ConfigItem<>("port", "p", Integer::parseInt, null, 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("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("user", new ConfigItem<>("user", "u", Function.identity())); + items.put("password", new ConfigItem<>("password", "pw", String::toCharArray)); } /** @@ -95,10 +99,12 @@ public class Config { } /** - * @return {@code true} if server, port and localDB directory are known. + * @return {@code true} if all mandatory config items are initialized * @since Envoy v0.1-alpha */ - public boolean isInitialized() { return items.values().stream().map(ConfigItem::get).noneMatch(Objects::isNull); } + public boolean isInitialized() { + return items.values().stream().filter(ConfigItem::isMandatory).map(ConfigItem::get).noneMatch(Objects::isNull); + } /** * @return the host name of the Envoy server @@ -141,4 +147,35 @@ public class Config { * @since Envoy v0.2-alpha */ public Level getConsoleLevelBarrier() { return (Level) items.get("consoleLevelBarrier").get(); } + + /** + * @return the user name + * @since Envoy v0.3-alpha + */ + public String getUser() { return (String) items.get("user").get(); } + + /** + * @return the password + * @since Envoy v0.3-alpha + */ + public char[] getPassword() { return (char[]) items.get("password").get(); } + + /** + * @return {@code true} if user name and password are set + * @since Envoy 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 v0.3-alpha + */ + public LoginCredentials getLoginCredentials() { + try { + return new LoginCredentials(getUser(), getPassword(), false); + } catch (NoSuchAlgorithmException e) { + return null; + } + } } \ No newline at end of file diff --git a/src/main/java/envoy/client/ConfigItem.java b/src/main/java/envoy/client/data/ConfigItem.java similarity index 56% rename from src/main/java/envoy/client/ConfigItem.java rename to src/main/java/envoy/client/data/ConfigItem.java index 87e927b..440a81c 100644 --- a/src/main/java/envoy/client/ConfigItem.java +++ b/src/main/java/envoy/client/data/ConfigItem.java @@ -1,4 +1,4 @@ -package envoy.client; +package envoy.client.data; import java.util.function.Function; @@ -9,49 +9,70 @@ import java.util.function.Function; * Project: envoy-clientChess
* File: ConfigItem.javaEvent.java
* Created: 21.12.2019
- * + * * @author Kai S. K. Engelbart * @param the type of the config item's value * @since Envoy v0.2-alpha */ public class ConfigItem { - private String commandLong, commandShort; - private Function parseFunction; - private T value; + private final String commandLong, commandShort; + private final Function parseFunction; + private final boolean mandatory; + + private T value; /** * Initializes a {@link ConfigItem} - * + * * @param commandLong the long command line argument to set this value * @param commandShort the short command line argument to set this value * @param parseFunction the {@code Function} that parses the value * from a string * @param defaultValue the optional default value to set before parsing + * @param mandatory indicated that this config item must be initialized with + * a non-null value * @since Envoy v0.2-alpha */ - public ConfigItem(String commandLong, String commandShort, Function parseFunction, T defaultValue) { + public ConfigItem(String commandLong, String commandShort, Function parseFunction, T defaultValue, boolean mandatory) { this.commandLong = commandLong; this.commandShort = commandShort; this.parseFunction = parseFunction; + this.mandatory = mandatory; value = defaultValue; } + /** + * Initializes an optional {@link ConfigItem} without a default value. + * + * @param commandLong the long command line argument to set this value + * @param commandShort the short command line argument to set this value + * @param parseFunction the {@code Function} that parses the value + * from a string + * @since Envoy v0.3-alpha + */ + public ConfigItem(String commandLong, String commandShort, Function parseFunction) { + this(commandLong, commandShort, parseFunction, null, false); + } + /** * Parses this {@ConfigItem}'s value from a string + * * @param input the string to parse from * @since Envoy v0.2-alpha */ public void parse(String input) { value = parseFunction.apply(input); } /** - * @return The long command line argument to set the value of this {@link ConfigItem} + * @return The long command line argument to set the value of this + * {@link ConfigItem} * @since Envoy v0.2-alpha */ public String getCommandLong() { return commandLong; } /** - * @return The short command line argument to set the value of this {@link ConfigItem} + * @return The short command line argument to set the value of this + * {@link ConfigItem} * @since Envoy v0.2-alpha */ public String getCommandShort() { return commandShort; } @@ -61,4 +82,11 @@ public class ConfigItem { * @since Envoy v0.2-alpha */ public T get() { return value; } + + /** + * @return {@code true} if this {@link ConfigItem} is mandatory for successful + * application initialization + * @since Envoy v0.3-alpha + */ + public boolean isMandatory() { return mandatory; } } diff --git a/src/main/java/envoy/client/data/PersistentLocalDb.java b/src/main/java/envoy/client/data/PersistentLocalDb.java index d6e594a..0a04729 100644 --- a/src/main/java/envoy/client/data/PersistentLocalDb.java +++ b/src/main/java/envoy/client/data/PersistentLocalDb.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import envoy.client.ConfigItem; import envoy.data.IdGenerator; import envoy.util.SerializationUtils; diff --git a/src/main/java/envoy/client/net/Client.java b/src/main/java/envoy/client/net/Client.java index 29b2f03..d742f1b 100644 --- a/src/main/java/envoy/client/net/Client.java +++ b/src/main/java/envoy/client/net/Client.java @@ -9,8 +9,8 @@ import java.util.logging.Logger; import javax.naming.TimeLimitExceededException; -import envoy.client.Config; import envoy.client.data.Cache; +import envoy.client.data.Config; import envoy.client.data.LocalDb; import envoy.client.util.EnvoyLog; import envoy.data.*; diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 46f7951..13f8676 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -11,7 +11,6 @@ import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; -import envoy.client.Config; import envoy.client.Settings; import envoy.client.data.*; import envoy.client.net.Client; @@ -63,7 +62,7 @@ public class Startup { // Override configuration values with command line arguments if (args.length > 0) config.load(args); - // Check if all configuration values have been initialized + // Check if all mandatory configuration values have been initialized if (!config.isInitialized()) throw new EnvoyException("Server or port are not defined"); } catch (Exception e) { JOptionPane @@ -76,8 +75,8 @@ public class Startup { EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier()); EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier()); - // Ask the user for their user name and password - LoginCredentials credentials = new LoginDialog().getCredentials(); + // Acquire login credentials + LoginCredentials credentials = config.hasLoginCredentials() ? config.getLoginCredentials() : new LoginDialog().getCredentials(); if (credentials == null) { logger.info("The login process has been aborted by the user. Exiting..."); diff --git a/src/main/java/envoy/client/util/EnvoyLog.java b/src/main/java/envoy/client/util/EnvoyLog.java index b59b714..ae6c6e5 100644 --- a/src/main/java/envoy/client/util/EnvoyLog.java +++ b/src/main/java/envoy/client/util/EnvoyLog.java @@ -4,7 +4,7 @@ import java.io.File; import java.io.IOException; import java.util.logging.*; -import envoy.client.Config; +import envoy.client.data.Config; /** * Project: envoy-client
From e1f248c2b846b81fbfe3f7a5cb4f8d157588b7c5 Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 7 Feb 2020 09:39:15 +0100 Subject: [PATCH 6/7] Added logging statement to WriteProxy#flushCache() --- src/main/java/envoy/client/net/WriteProxy.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/envoy/client/net/WriteProxy.java b/src/main/java/envoy/client/net/WriteProxy.java index 454751f..98206ae 100644 --- a/src/main/java/envoy/client/net/WriteProxy.java +++ b/src/main/java/envoy/client/net/WriteProxy.java @@ -62,10 +62,11 @@ public class WriteProxy { /** * Sends cached {@link Message}s and {@link MessageStatusChangeEvent}s to the * server. - * + * * @since Envoy v0.3-alpha */ public void flushCache() { + logger.info("Sending cached messages and message status change events..."); // Send messages localDb.getMessageCache().relay(); From 120ba8a60dcaf02519c81b3a7f231ad056cd763a Mon Sep 17 00:00:00 2001 From: Haramus Samsamus Date: Fri, 7 Feb 2020 13:44:09 +0100 Subject: [PATCH 7/7] Fixed Javadoc errors --- src/main/java/envoy/client/data/ConfigItem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/envoy/client/data/ConfigItem.java b/src/main/java/envoy/client/data/ConfigItem.java index 440a81c..6a987cb 100644 --- a/src/main/java/envoy/client/data/ConfigItem.java +++ b/src/main/java/envoy/client/data/ConfigItem.java @@ -23,7 +23,7 @@ public class ConfigItem { private T value; /** - * Initializes a {@link ConfigItem} + * Initializes a {@link ConfigItem}. * * @param commandLong the long command line argument to set this value * @param commandShort the short command line argument to set this value @@ -56,7 +56,7 @@ public class ConfigItem { } /** - * Parses this {@ConfigItem}'s value from a string + * Parses this {@ConfigItem}'s value from a string. * * @param input the string to parse from * @since Envoy v0.2-alpha