From 2eb9c79106b4b66a03e1644ecb8f2b1b67873fd5 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sat, 14 Dec 2019 10:53:20 +0100 Subject: [PATCH] Implemented offline mode for Client and LocalDB --- src/main/java/envoy/client/Client.java | 28 +++++---- src/main/java/envoy/client/LocalDB.java | 34 +++++------ src/main/java/envoy/client/ui/ChatWindow.java | 31 ++++------ src/main/java/envoy/client/ui/Startup.java | 57 ++++++++++++++----- 4 files changed, 87 insertions(+), 63 deletions(-) diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index fdf371d..356dc24 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -1,6 +1,7 @@ package envoy.client; -import java.util.logging.Logger; +import java.util.HashMap; +import java.util.Map; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; @@ -32,13 +33,11 @@ public class Client { private User sender, recipient; private boolean online = false; - private static final Logger logger = Logger.getLogger(Client.class.getSimpleName()); + public Client(Config config) { this.config = config; } - public Client(Config config, String userName) throws EnvoyException { - this.config = config; - sender = getUser(userName); - - logger.info("Client user ID: " + sender.getID()); + public void onlineInit(String userName) throws EnvoyException { + sender = getUser(userName); + online = true; } private R post(String uri, T body, Class responseBodyClass) { @@ -53,22 +52,25 @@ public class Client { } /** - * Returns a {@link Sync} with all users on the server. + * Returns a {@code Map} of all users on the server with their + * user names as keys. * * @return Sync - List of all users on the server. - * @since Envoy v0.1-alpha + * @since Envoy v0.2-alpha */ - public Sync getUsersListXml() { + public Map getUsers() { Sync sendSync = objectFactory.createSync(); User user = objectFactory.createUser(); user.setID(-1); sendSync.getUsers().add(user); - Sync returnSendSync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), + Sync returnSync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), sendSync, Sync.class); - return returnSendSync; + Map users = new HashMap<>(); + returnSync.getUsers().forEach(u -> users.put(u.getName(), u)); + return users; } /** @@ -160,6 +162,8 @@ public class Client { */ public User getSender() { return sender; } + public void setSender(User sender) { this.sender = sender; } + /** * @return the current recipient of the current chat. * @since Envoy v0.1-alpha diff --git a/src/main/java/envoy/client/LocalDB.java b/src/main/java/envoy/client/LocalDB.java index 2ab33ed..f22c7c7 100644 --- a/src/main/java/envoy/client/LocalDB.java +++ b/src/main/java/envoy/client/LocalDB.java @@ -8,7 +8,9 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Logger; import javax.xml.datatype.DatatypeConfigurationException; @@ -34,9 +36,9 @@ import envoy.schema.User; */ public class LocalDB { - private File localDBFile, usersFile; + private File localDBDir, localDBFile, usersFile; private User user; - private List users = new ArrayList<>(); + private Map users = new HashMap<>(); private List chats = new ArrayList<>(); private ObjectFactory objectFactory = new ObjectFactory(); @@ -54,12 +56,19 @@ public class LocalDB { * * @since Envoy v0.1-alpha */ - public LocalDB() { + public LocalDB(File localDBDir) throws IOException { + this.localDBDir = localDBDir; + try { datatypeFactory = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException e) { e.printStackTrace(); } + + // 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"); } /** @@ -70,18 +79,9 @@ public class LocalDB { * @throws EnvoyException if the directory selected is not an actual directory. * @since Envoy v0.1-alpha */ - public void initializeDBFile(File localDBDir) throws EnvoyException { + public void initializeDBFile() throws EnvoyException { if (user == null) throw new NullPointerException("Client user is null"); - if (localDBDir.exists() && !localDBDir.isDirectory()) - throw new EnvoyException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); - usersFile = new File(localDBDir, "users.db"); localDBFile = new File(localDBDir, user.getID() + ".db"); - try { - loadUsers(); - loadChats(); - } catch (ClassNotFoundException | IOException e) { - throw new EnvoyException(e); - } } /** @@ -99,7 +99,7 @@ public class LocalDB { } @SuppressWarnings("unchecked") - private void loadUsers() throws ClassNotFoundException, IOException { users = read(usersFile, ArrayList.class); } + public void loadUsers() throws ClassNotFoundException, IOException { users = read(usersFile, HashMap.class); } /** * Loads all chats saved by Envoy for the client user. @@ -110,7 +110,7 @@ public class LocalDB { * @since Envoy v0.1-alpha */ @SuppressWarnings("unchecked") - private void loadChats() throws ClassNotFoundException, IOException { chats = read(localDBFile, ArrayList.class); } + public void loadChats() throws ClassNotFoundException, IOException { chats = read(localDBFile, ArrayList.class); } private T read(File file, Class serializedClass) throws ClassNotFoundException, IOException { try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) { @@ -308,12 +308,12 @@ public class LocalDB { /** * @return the users */ - public List getUsers() { return users; } + public Map getUsers() { return users; } /** * @param users the users to set */ - public void setUsers(List users) { this.users = users; } + public void setUsers(Map users) { this.users = users; } /** * @return all saved {@link Chat} objects that list the client user as the diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index ee32bf6..0c85abb 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -34,7 +34,6 @@ import envoy.client.Config; import envoy.client.LocalDB; import envoy.client.Settings; import envoy.schema.Message; -import envoy.schema.Sync; import envoy.schema.User; /** @@ -132,8 +131,7 @@ public class ChatWindow extends JFrame { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER - && ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) - || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) { + && ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) { postMessage(messageList); } } @@ -181,8 +179,8 @@ public class ChatWindow extends JFrame { settingsButton.addActionListener((evt) -> { try { SettingsScreen.open(); - changeChatWindowColors(Settings.getInstance().getCurrentTheme()); - } catch (Exception e) { + changeChatWindowColors(Settings.getInstance().getCurrentTheme()); + } catch (Exception e) { SettingsScreen.open(); logger.log(Level.WARNING, "An error occured while opening the settings screen", e); e.printStackTrace(); @@ -235,7 +233,7 @@ public class ChatWindow extends JFrame { gbc_userList.insets = new Insets(space, space, space, space); changeChatWindowColors(Settings.getInstance().getCurrentTheme()); - + contentPane.add(userList, gbc_userList); contentPane.revalidate(); @@ -244,7 +242,6 @@ public class ChatWindow extends JFrame { contentPane.revalidate(); } - /** * Used to immediately reload the ChatWindow when settings were changed. @@ -287,18 +284,14 @@ public class ChatWindow extends JFrame { private void postMessage(JList messageList) { if (!client.hasRecipient()) { - JOptionPane.showMessageDialog(this, - "Please select a recipient!", - "Cannot send message", - JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE); return; } if (!messageEnterTextArea.getText().isEmpty()) try { // Create and send message object - final Message message = localDB.createMessage(messageEnterTextArea.getText(), - currentChat.getRecipient().getID()); + final Message message = localDB.createMessage(messageEnterTextArea.getText(), currentChat.getRecipient().getID()); currentChat.appendMessage(message); messageList.setModel(currentChat.getModel()); @@ -322,9 +315,8 @@ public class ChatWindow extends JFrame { */ private void loadUsersAndChats() { new Thread(() -> { - Sync users = client.getUsersListXml(); - DefaultListModel userListModel = new DefaultListModel<>(); - users.getUsers().forEach(user -> { + DefaultListModel userListModel = new DefaultListModel<>(); + localDB.getUsers().values().forEach(user -> { userListModel.addElement(user); // Check if user exists in local DB @@ -348,8 +340,7 @@ public class ChatWindow extends JFrame { new Thread(() -> { // Synchronize - localDB.applySync( - client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID()))); + localDB.applySync(client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID()))); // Process unread messages localDB.addUnreadMessagesToLocalDB(); @@ -359,8 +350,7 @@ public class ChatWindow extends JFrame { readCurrentChat(); // Update UI - SwingUtilities - .invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); }); + SwingUtilities.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); }); }).start(); }).start(); } @@ -377,4 +367,3 @@ public class ChatWindow extends JFrame { */ private void readCurrentChat() { if (currentChat != null) { localDB.setMessagesToRead(currentChat); } } } - diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index b9d08c4..e2f6b5e 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -1,6 +1,7 @@ package envoy.client.ui; import java.awt.EventQueue; +import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -10,6 +11,7 @@ import envoy.client.Client; import envoy.client.Config; import envoy.client.LocalDB; import envoy.exception.EnvoyException; +import envoy.schema.User; /** * Starts the Envoy client and prompts the user to enter their name. @@ -54,32 +56,61 @@ public class Startup { logger.severe("User name is not set or empty. Exiting..."); System.exit(1); } - - // Acquire the client user (with ID) either from the server or from the local - // database, which triggers offline mode - Client client; + + // Initialize the local database + LocalDB localDB; try { - client = new Client(config, userName); - } catch (Exception e1) { - logger.log(Level.SEVERE, "Failed to acquire client user ID", e1); - JOptionPane.showMessageDialog(null, e1.toString(), "Client error", JOptionPane.ERROR_MESSAGE); + localDB = new LocalDB(config.getLocalDB()); + } catch (IOException e3) { + logger.log(Level.SEVERE, "Could not initialize local database", e3); + JOptionPane.showMessageDialog(null, "Could not initialize local database!\n" + e3.toString()); System.exit(1); return; } - // Load the local database - LocalDB localDB = new LocalDB(); - localDB.setUser(client.getSender()); + // Acquire the client user (with ID) either from the server or from the local + // database, which triggers offline mode + Client client = new Client(config); try { - localDB.initializeDBFile(config.getLocalDB()); - } catch (EnvoyException e) { + // Try entering online mode first + client.onlineInit(userName); + } catch (Exception e1) { + logger.warning("Could not connect to server. Trying offline mode..."); + try { + // Try entering offline mode + localDB.loadUsers(); + User clientUser = localDB.getUsers().get(userName); + if(clientUser == null) + throw new EnvoyException("Could not enter offline mode: user name unknown"); + client.setSender(clientUser); + } catch(Exception e2) { + JOptionPane.showMessageDialog(null, e1.toString(), "Client error", JOptionPane.ERROR_MESSAGE); + System.exit(1); + return; + } + } + + // Set client user in local database + localDB.setUser(client.getSender()); + + // Initialize chats in local database + try { + localDB.initializeDBFile(); + localDB.loadChats(); + } catch (EnvoyException | ClassNotFoundException | IOException e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "Error while loading local database: " + e.toString() + "\nChats will not be stored locally.", "Local DB error", JOptionPane.WARNING_MESSAGE); } + + logger.info("Client user ID: " + client.getSender().getID()); + // Save all users to the local database + if(client.isOnline()) + localDB.setUsers(client.getUsers()); + EventQueue.invokeLater(() -> { try { ChatWindow chatWindow = new ChatWindow(client, localDB);