From 117f9043cedb7f334824533c7b5f4202424e667c Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Wed, 11 Dec 2019 18:52:30 +0100 Subject: [PATCH 1/7] Added offline mode to Client + Saving the user ID in Settings + Loading the user ID from Settings in Client if the server can't be reached --- src/main/java/envoy/client/Client.java | 63 ++++++++++++---------- src/main/java/envoy/client/Settings.java | 25 ++++++--- src/main/java/envoy/client/ui/Startup.java | 21 ++++++-- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index 8f96739..a6a7f36 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -10,6 +10,7 @@ import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; +import envoy.exception.EnvoyException; import envoy.schema.ObjectFactory; import envoy.schema.Sync; import envoy.schema.User; @@ -29,14 +30,18 @@ public class Client { private ObjectFactory objectFactory = new ObjectFactory(); private Config config; private User sender, recipient; + private boolean online = false; private static final Logger logger = Logger.getLogger(Client.class.getSimpleName()); - public Client(Config config, String username) { + public Client(Config config, String userName) throws EnvoyException { this.config = config; - sender = getUser(username); - - logger.info("ID: " + sender.getID()); + sender = getUser(userName); + + // Update the user ID in the cache + Settings.getInstance().setUserID(sender.getID()); + + logger.info("Client user ID: " + sender.getID()); } private R post(String uri, T body, Class responseBodyClass) { @@ -48,7 +53,6 @@ public class Client { client.close(); return responseBody; - } /** @@ -63,9 +67,7 @@ public class Client { 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 returnSendSync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), sendSync, Sync.class); return returnSendSync; @@ -77,29 +79,33 @@ public class Client { * * @param name - the name of the {@link User} * @return a {@link User} with the specified name + * @throws EnvoyException if the server does not return the requested ID * @since Envoy v0.1-alpha */ - private User getUser(String name) { + private User getUser(String name) throws EnvoyException { + // Create a sync with only a user with the requested name Sync senderSync = objectFactory.createSync(); User user = objectFactory.createUser(); user.setName(name); senderSync.getUsers().add(user); - Sync returnSenderSync = post( - String - .format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), - senderSync, - Sync.class); + try { + Sync sync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), + senderSync, + Sync.class); - User returnSender = objectFactory.createUser(); - - if (returnSenderSync.getUsers().size() == 1) { - returnSender = returnSenderSync.getUsers().get(0); - } else { - logger.warning("ERROR exiting..."); + // Expecting a single user with an ID + if (sync.getUsers().size() == 1) { + online = true; + return sync.getUsers().get(0); + } else throw new EnvoyException("Unexpected response from Envoy Server"); + } catch (Exception e) { + logger.warning("Could not connect to server, trying offline mode."); + if (Settings.getInstance().getUserID() != -1) { + user.setID(Settings.getInstance().getUserID()); + return user; + } else throw new EnvoyException("Could not enter offline mode."); } - - return returnSender; } /** @@ -134,7 +140,8 @@ public class Client { * their updated UserStatus to the client.)
* * @param userId the id of the {@link Client} who sends the {@link Sync} - * @param sync the sync object (yet to be converted from java class to sync.xml) + * @param sync the sync object (yet to be converted from java class to + * sync.xml) * @return a returnSync.xml file * @since Envoy v0.1-alpha */ @@ -151,10 +158,7 @@ public class Client { } // Send sync - return post(String - .format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), userId), - sync, - Sync.class); + return post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), userId), sync, Sync.class); } /** @@ -182,4 +186,9 @@ public class Client { * @since Envoy v0.1-alpha */ public boolean hasRecipient() { return recipient != null; } + + /** + * @return {@code true} if a connection to the server could be established + */ + public boolean isOnline() { return online; } } diff --git a/src/main/java/envoy/client/Settings.java b/src/main/java/envoy/client/Settings.java index 91f696f..851c936 100644 --- a/src/main/java/envoy/client/Settings.java +++ b/src/main/java/envoy/client/Settings.java @@ -27,6 +27,7 @@ public class Settings { // Actual settings accessible by the rest of the application private String username; + private long userID; private String email; private boolean enterToSend = true; private Map themes; @@ -66,6 +67,7 @@ public class Settings { @SuppressWarnings("unchecked") private void load() { setUsername(prefs.get("username", "")); + setUserID(prefs.getLong("userID", -1)); setEmail(prefs.get("email", "")); setEnterToSend(prefs.getBoolean("enterToSend", true)); setCurrentTheme(prefs.get("theme", "dark")); @@ -73,9 +75,9 @@ public class Settings { // Load themes from theme file try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(themeFile))) { Object obj = in.readObject(); - if(obj instanceof HashMap) themes = (Map) obj; + if (obj instanceof HashMap) themes = (Map) obj; } catch (IOException | ClassNotFoundException e) { - themes = new HashMap<>(); + themes = new HashMap<>(); currentTheme = "dark"; e.printStackTrace(); } @@ -90,20 +92,21 @@ public class Settings { } /** - * updates prefs when save button is clicked + * Updates the preferences when the save button is clicked. * * @throws IOException * @since Envoy v0.2-alpha */ - public void save() throws IOException{ + public void save() throws IOException { prefs.put("username", getUsername()); + prefs.putLong("userID", getUserID()); prefs.put("email", getEmail()); prefs.put("theme", currentTheme); prefs.putBoolean("enterToSend", isEnterToSend()); - + // Save themes to theme file themeFile.createNewFile(); - try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(themeFile))) { + try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(themeFile))) { out.writeObject(themes); } } @@ -145,6 +148,16 @@ public class Settings { */ public void setUsername(String username) { this.username = username; } + /** + * @return the userID + */ + public long getUserID() { return userID; } + + /** + * @param userID the userID to set + */ + public void setUserID(long userID) { this.userID = userID; } + /** * @return the email associated with that user. * @since Envoy v0.2-alpha diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index edd5aa2..c8373ed 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -16,7 +16,7 @@ import envoy.exception.EnvoyException; /** * Starts the Envoy client and prompts the user to enter their name. - * + *
* Project: envoy-client
* File: Startup.java
* Created: 12 Oct 2019
@@ -48,19 +48,34 @@ 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 if (!config.isInitialized()) { logger.severe("Server or port are not defined. Exiting..."); JOptionPane.showMessageDialog(null, "Error loading configuration values.", "Configuration error", JOptionPane.ERROR_MESSAGE); System.exit(1); } + // Ask the user for their user name String userName = JOptionPane.showInputDialog("Please enter your username"); if (userName == null || userName.isEmpty()) { logger.severe("User name is not set or empty. Exiting..."); System.exit(1); } - Client client = new Client(config, userName); - LocalDB localDB = new LocalDB(client.getSender()); + + // Acquire the client user (with ID) either from the server or from the local + // cache (Preferences), which triggers offline mode + Client client; + 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); + System.exit(1); + return; + } + + // Load the local database + LocalDB localDB = new LocalDB(client.getSender()); try { localDB.initializeDBFile(config.getLocalDB()); } catch (EnvoyException e) { From daca66d729c8f0b12e49d7d5f27d2163527bdb74 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Fri, 13 Dec 2019 08:50:15 +0100 Subject: [PATCH 2/7] Moved client.properties loading from Startup to Config --- src/main/java/envoy/client/Config.java | 38 ++++++++++++++-------- src/main/java/envoy/client/ui/Startup.java | 25 +++++--------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/main/java/envoy/client/Config.java b/src/main/java/envoy/client/Config.java index bbd3b72..0c3a000 100644 --- a/src/main/java/envoy/client/Config.java +++ b/src/main/java/envoy/client/Config.java @@ -3,6 +3,8 @@ package envoy.client; import java.io.File; import java.util.Properties; +import envoy.exception.EnvoyException; + /** * Project: envoy-client
* File: Config.java
@@ -29,17 +31,26 @@ public class Config { /** * Defaults to the {@code client.properties} file for information. + * This file contains information about + * the server and port, as well as the path to the local + * database and the synchronization timeout * - * @param properties a {@link Properties} object containing information about - * the server and port, as well as the path to the local - * database and the synchronization timeout + * @throws EnvoyException if the {@code client.properties} file could not be + * loaded * @since Envoy v0.1-alpha */ - public void load(Properties properties) { - if (properties.containsKey("server")) server = properties.getProperty("server"); - if (properties.containsKey("port")) port = Integer.parseInt(properties.getProperty("port")); - localDB = new File(properties.getProperty("localDB", ".\\localDB")); - syncTimeout = Integer.parseInt(properties.getProperty("syncTimeout", "1000")); + public void load() throws EnvoyException { + ClassLoader loader = getClass().getClassLoader(); + try { + Properties properties = new Properties(); + properties.load(loader.getResourceAsStream("client.properties")); + if (properties.containsKey("server")) server = properties.getProperty("server"); + if (properties.containsKey("port")) port = Integer.parseInt(properties.getProperty("port")); + localDB = new File(properties.getProperty("localDB", ".\\localDB")); + syncTimeout = Integer.parseInt(properties.getProperty("syncTimeout", "1000")); + } catch (Exception e) { + throw new EnvoyException("Failed to load client.properties", e); + } } /** @@ -47,9 +58,10 @@ public class Config { * -s, --port / -p and --localDB / -db. * * @param args the command line arguments to parse + * @throws EnvoyException if the command line arguments contain an unknown token * @since Envoy v0.1-alpha */ - public void load(String[] args) { + public void load(String[] args) throws EnvoyException { for (int i = 0; i < args.length; i++) switch (args[i]) { case "--server": @@ -64,6 +76,8 @@ public class Config { case "-db": localDB = new File(args[++i]); break; + default: + throw new EnvoyException("Unknown token " + args[i] + " found"); } } @@ -71,9 +85,7 @@ public class Config { * @return {@code true} if server, port and localDB directory are known. * @since Envoy v0.1-alpha */ - public boolean isInitialized() { - return server != null && !server.isEmpty() && port > 0 && port < 65566 && localDB != null; - } + public boolean isInitialized() { return server != null && !server.isEmpty() && port > 0 && port < 65566 && localDB != null; } /** * @return the host name of the Envoy server @@ -114,7 +126,7 @@ public class Config { /** * Changes the default local database. * Exclusively intended for development purposes. - * + * * @param localDB the file containing the local database * @since Envoy v0.1-alpha **/ diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index c8373ed..48ce399 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -1,8 +1,6 @@ package envoy.client.ui; import java.awt.EventQueue; -import java.io.IOException; -import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; @@ -35,24 +33,19 @@ public class Startup { Config config = Config.getInstance(); - // Load the configuration from client.properties first - ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { - Properties configProperties = new Properties(); - configProperties.load(loader.getResourceAsStream("client.properties")); - config.load(configProperties); - } catch (IOException e) { - e.printStackTrace(); - } + // Load the configuration from client.properties first + config.load(); - // Override configuration values with command line arguments - if (args.length > 0) config.load(args); + // Override configuration values with command line arguments + if (args.length > 0) config.load(args); - // Check if all configuration values have been initialized - if (!config.isInitialized()) { - logger.severe("Server or port are not defined. Exiting..."); - JOptionPane.showMessageDialog(null, "Error loading configuration values.", "Configuration error", JOptionPane.ERROR_MESSAGE); + // Check if all configuration values have been initialized + if (!config.isInitialized()) throw new EnvoyException("Server or port are not defined"); + } catch (Exception e) { + JOptionPane.showMessageDialog(null, "Error loading configuration values: \n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); System.exit(1); + e.printStackTrace(); } // Ask the user for their user name From f983b6ffe798815b3194196e51b58c00cbbd7bda Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sat, 14 Dec 2019 08:44:03 +0100 Subject: [PATCH 3/7] Added user list serialization to LocalDB * Added user list to LocalDB * Removed client user from LocalDB constructor --- src/main/java/envoy/client/LocalDB.java | 108 +++++++++++++----- src/main/java/envoy/client/ui/ChatWindow.java | 2 +- src/main/java/envoy/client/ui/Startup.java | 3 +- 3 files changed, 80 insertions(+), 33 deletions(-) diff --git a/src/main/java/envoy/client/LocalDB.java b/src/main/java/envoy/client/LocalDB.java index f061b28..2ab33ed 100644 --- a/src/main/java/envoy/client/LocalDB.java +++ b/src/main/java/envoy/client/LocalDB.java @@ -34,9 +34,11 @@ import envoy.schema.User; */ public class LocalDB { - private File localDB; - private User sender; - private List chats = new ArrayList<>(); + private File localDBFile, usersFile; + private User user; + private List users = new ArrayList<>(); + private List chats = new ArrayList<>(); + private ObjectFactory objectFactory = new ObjectFactory(); private DatatypeFactory datatypeFactory; @@ -44,16 +46,15 @@ public class LocalDB { private Sync sync = objectFactory.createSync(); private Sync readMessages = objectFactory.createSync(); - private static final Logger logger = Logger.getLogger(LocalDB.class.getSimpleName()); + private static final Logger logger = Logger.getLogger(LocalDB.class.getSimpleName()); /** - * Constructs an empty local database. + * Constructs an empty local database. To serialize any chats to the file + * system, call {@link LocalDB#initializeDBFile(File)}. * - * @param sender the user that is logged in with this client * @since Envoy v0.1-alpha */ - public LocalDB(User sender) { - this.sender = sender; + public LocalDB() { try { datatypeFactory = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException e) { @@ -70,10 +71,17 @@ public class LocalDB { * @since Envoy v0.1-alpha */ public void initializeDBFile(File localDBDir) 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())); - localDB = new File(localDBDir, sender.getID() + ".db"); - if (localDB.exists()) loadFromLocalDB(); + usersFile = new File(localDBDir, "users.db"); + localDBFile = new File(localDBDir, user.getID() + ".db"); + try { + loadUsers(); + loadChats(); + } catch (ClassNotFoundException | IOException e) { + throw new EnvoyException(e); + } } /** @@ -82,29 +90,46 @@ public class LocalDB { * @throws IOException if something went wrong during saving * @since Envoy v0.1-alpha */ - public void saveToLocalDB() throws IOException { - localDB.getParentFile().mkdirs(); - localDB.createNewFile(); - try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(localDB))) { - out.writeObject(chats); - } catch (IOException ex) { - throw ex; - } + public void save() throws IOException { + // Save users + write(usersFile, users); + + // Save chats + write(localDBFile, chats); } + @SuppressWarnings("unchecked") + private void loadUsers() throws ClassNotFoundException, IOException { users = read(usersFile, ArrayList.class); } + /** * Loads all chats saved by Envoy for the client user. * - * @throws EnvoyException if something fails while loading. + * @throws EnvoyException if something fails while loading. + * @throws IOException + * @throws ClassNotFoundException * @since Envoy v0.1-alpha */ @SuppressWarnings("unchecked") - private void loadFromLocalDB() throws EnvoyException { - try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(localDB))) { - Object obj = in.readObject(); - if (obj instanceof ArrayList) chats = (ArrayList) obj; + private 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))) { + return serializedClass.cast(in.readObject()); } catch (ClassNotFoundException | IOException e) { - throw new EnvoyException(e); + throw e; + } + } + + private void write(File file, T obj) throws IOException { + if (file == null) throw new NullPointerException("File is null"); + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) { + out.writeObject(obj); + } catch (IOException e) { + throw e; } } @@ -118,7 +143,7 @@ public class LocalDB { */ public Message createMessage(String textContent, long recipientID) { Message.Metadata metaData = objectFactory.createMessageMetadata(); - metaData.setSender(sender.getID()); + metaData.setSender(user.getID()); metaData.setRecipient(recipientID); metaData.setState(MessageState.WAITING); metaData.setDate(datatypeFactory.newXMLGregorianCalendar(Instant.now().toString())); @@ -134,13 +159,13 @@ public class LocalDB { return message; } - /** + /** * Creates a {@link Sync} object filled with the changes that occurred to the * local database since the last synchronization. * * @param userId the ID of the user that is synchronized by this client * @return {@link Sync} object filled with the current changes - * @since Envoy v0.1-alpha + * @since Envoy v0.1-alpha */ public Sync fillSync(long userId) { addWaitingMessagesToSync(); @@ -156,7 +181,7 @@ public class LocalDB { * Applies the changes carried by a {@link Sync} object to the local database * * @param returnSync the {@link Sync} object to apply - * @since Envoy v0.1-alpha + * @since Envoy v0.1-alpha */ public void applySync(Sync returnSync) { for (int i = 0; i < returnSync.getMessages().size(); i++) { @@ -280,6 +305,16 @@ public class LocalDB { */ public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); } + /** + * @return the users + */ + public List getUsers() { return users; } + + /** + * @param users the users to set + */ + public void setUsers(List users) { this.users = users; } + /** * @return all saved {@link Chat} objects that list the client user as the * sender @@ -288,8 +323,19 @@ public class LocalDB { public List getChats() { return chats; } /** - * @return the {@link User} who initialized the local database - * @since Envoy v0.1-alpha + * @param chats the chats to set */ - public User getUser() { return sender; } + public void setChats(List chats) { this.chats = chats; } + + /** + * @return the {@link User} who initialized the local database + * @since Envoy v0.2-alpha + */ + public User getUser() { return user; } + + /** + * @param user the user to set + * @since Envoy v0.2-alpha + */ + public void setUser(User user) { this.user = user; } } diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 6867f5e..ee32bf6 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -86,7 +86,7 @@ public class ChatWindow extends JFrame { @Override public void windowClosing(WindowEvent evt) { try { - localDB.saveToLocalDB(); + localDB.save(); Settings.getInstance().save(); } catch (IOException e1) { e1.printStackTrace(); diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 48ce399..f0cf53e 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -68,7 +68,8 @@ public class Startup { } // Load the local database - LocalDB localDB = new LocalDB(client.getSender()); + LocalDB localDB = new LocalDB(); + localDB.setUser(client.getSender()); try { localDB.initializeDBFile(config.getLocalDB()); } catch (EnvoyException e) { From 27c420afdccb7d6539e8bad2c57ba99fa918122f Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sat, 14 Dec 2019 09:58:25 +0100 Subject: [PATCH 4/7] Removed user id, name and email from Settings --- src/main/java/envoy/client/Client.java | 9 +- src/main/java/envoy/client/Settings.java | 43 --------- .../java/envoy/client/ui/SettingsScreen.java | 91 ++++--------------- src/main/java/envoy/client/ui/Startup.java | 7 +- 4 files changed, 20 insertions(+), 130 deletions(-) diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index a6a7f36..fdf371d 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -38,9 +38,6 @@ public class Client { this.config = config; sender = getUser(userName); - // Update the user ID in the cache - Settings.getInstance().setUserID(sender.getID()); - logger.info("Client user ID: " + sender.getID()); } @@ -100,11 +97,7 @@ public class Client { return sync.getUsers().get(0); } else throw new EnvoyException("Unexpected response from Envoy Server"); } catch (Exception e) { - logger.warning("Could not connect to server, trying offline mode."); - if (Settings.getInstance().getUserID() != -1) { - user.setID(Settings.getInstance().getUserID()); - return user; - } else throw new EnvoyException("Could not enter offline mode."); + throw new EnvoyException("Could not connect to server", e); } } diff --git a/src/main/java/envoy/client/Settings.java b/src/main/java/envoy/client/Settings.java index 851c936..714e5f7 100644 --- a/src/main/java/envoy/client/Settings.java +++ b/src/main/java/envoy/client/Settings.java @@ -26,9 +26,6 @@ import envoy.client.ui.Theme; public class Settings { // Actual settings accessible by the rest of the application - private String username; - private long userID; - private String email; private boolean enterToSend = true; private Map themes; private String currentTheme; @@ -66,9 +63,6 @@ public class Settings { @SuppressWarnings("unchecked") private void load() { - setUsername(prefs.get("username", "")); - setUserID(prefs.getLong("userID", -1)); - setEmail(prefs.get("email", "")); setEnterToSend(prefs.getBoolean("enterToSend", true)); setCurrentTheme(prefs.get("theme", "dark")); @@ -98,9 +92,6 @@ public class Settings { * @since Envoy v0.2-alpha */ public void save() throws IOException { - prefs.put("username", getUsername()); - prefs.putLong("userID", getUserID()); - prefs.put("email", getEmail()); prefs.put("theme", currentTheme); prefs.putBoolean("enterToSend", isEnterToSend()); @@ -136,40 +127,6 @@ public class Settings { */ public void setCurrentTheme(String themeName) { currentTheme = themeName; } - /** - * @return the user name - * @since Envoy v0.2-alpha - */ - public String getUsername() { return username; } - - /** - * @param username the user name to set - * @since Envoy v0.2-alpha - */ - public void setUsername(String username) { this.username = username; } - - /** - * @return the userID - */ - public long getUserID() { return userID; } - - /** - * @param userID the userID to set - */ - public void setUserID(long userID) { this.userID = userID; } - - /** - * @return the email associated with that user. - * @since Envoy v0.2-alpha - */ - public String getEmail() { return email; } - - /** - * @param email the email to set - * @since Envoy v0.2-alpha - */ - public void setEmail(String email) { this.email = email; } - /** * @return true, if "enter" suffices to send a message, else it has to be "ctrl" * + "enter" diff --git a/src/main/java/envoy/client/ui/SettingsScreen.java b/src/main/java/envoy/client/ui/SettingsScreen.java index bd80c2e..fdc12dc 100644 --- a/src/main/java/envoy/client/ui/SettingsScreen.java +++ b/src/main/java/envoy/client/ui/SettingsScreen.java @@ -101,8 +101,7 @@ public class SettingsScreen extends JDialog { createNewThemeButton.setEnabled(false); - temporaryTheme = new Theme("temporaryTheme", - Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); + temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); // Content pane GridBagLayout gbl_contentPanel = new GridBagLayout(); @@ -200,20 +199,8 @@ public class SettingsScreen extends JDialog { colorsPanel.setLayout(new BoxLayout(colorsPanel, BoxLayout.Y_AXIS)); colorsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getBackgroundColor(), - "Background", - 1); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getCellColor(), - "Cells", - 2); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getBackgroundColor(), "Background", 1); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getCellColor(), "Cells", 2); buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), @@ -228,41 +215,11 @@ public class SettingsScreen extends JDialog { theme.getInteractableBackgroundColor(), "Interactable Background", 4); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getMessageColorChat(), - "Messages Chat", - 5); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getDateColorChat(), - "Date Chat", - 6); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getSelectionColor(), - "Selection", - 7); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getTypingMessageColor(), - "Typing Message", - 8); - buildCustomizeElement(new JPanel(), - new JButton(), - new JTextPane(), - theme, - theme.getUserNameColor(), - "User Names", - 9); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getMessageColorChat(), "Messages Chat", 5); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getDateColorChat(), "Date Chat", 6); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getSelectionColor(), "Selection", 7); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getTypingMessageColor(), "Typing Message", 8); + buildCustomizeElement(new JPanel(), new JButton(), new JTextPane(), theme, theme.getUserNameColor(), "User Names", 9); GridBagConstraints gbc_colorsPanel = new GridBagConstraints(); gbc_colorsPanel.fill = GridBagConstraints.HORIZONTAL; @@ -283,16 +240,14 @@ public class SettingsScreen extends JDialog { String s = JOptionPane.showInputDialog("Enter a name for the new theme"); System.out.println(s); Settings.getInstance() - .addNewThemeToMap(new Theme(s, temporaryTheme.getBackgroundColor(), - temporaryTheme.getCellColor(), temporaryTheme.getInteractableForegroundColor(), - temporaryTheme.getInteractableBackgroundColor(), temporaryTheme.getMessageColorChat(), - temporaryTheme.getDateColorChat(), temporaryTheme.getSelectionColor(), + .addNewThemeToMap(new Theme(s, temporaryTheme.getBackgroundColor(), temporaryTheme.getCellColor(), + temporaryTheme.getInteractableForegroundColor(), temporaryTheme.getInteractableBackgroundColor(), + temporaryTheme.getMessageColorChat(), temporaryTheme.getDateColorChat(), temporaryTheme.getSelectionColor(), temporaryTheme.getTypingMessageColor(), temporaryTheme.getUserNameColor())); themeArray = Arrays.copyOf(themeArray, themeArray.length + 1); themeArray[themeArray.length - 1] = Settings.getInstance().getThemes().get(s).getThemeName(); - temporaryTheme = new Theme("temporaryTheme", - Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); + temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); createNewThemeButton.setEnabled(false); themes.addItem(themeArray[themeArray.length - 1]); @@ -348,10 +303,6 @@ public class SettingsScreen extends JDialog { getRootPane().setDefaultButton(okButton); okButton.addActionListener((evt) -> { try { - Settings.getInstance().setUsername(Settings.getInstance().getUsername());// still temporary - - Settings.getInstance().setEmail(Settings.getInstance().getEmail());// still temporary value - Settings.getInstance().setEnterToSend(Settings.getInstance().isEnterToSend());// still temporary Settings.getInstance().setCurrentTheme(selectedTheme.getThemeName()); @@ -412,21 +363,12 @@ public class SettingsScreen extends JDialog { temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getBackgroundColor(), Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getCellColor(), - Settings.getInstance() - .getThemes() - .get(Settings.getInstance().getCurrentTheme()) - .getInteractableForegroundColor(), - Settings.getInstance() - .getThemes() - .get(Settings.getInstance().getCurrentTheme()) - .getInteractableBackgroundColor(), + Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getInteractableForegroundColor(), + Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getInteractableBackgroundColor(), Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getMessageColorChat(), Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat(), Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getSelectionColor(), - Settings.getInstance() - .getThemes() - .get(Settings.getInstance().getCurrentTheme()) - .getTypingMessageColor(), + Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getTypingMessageColor(), Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor()); colorsPanel.removeAll(); @@ -513,8 +455,7 @@ public class SettingsScreen extends JDialog { private void setContent(JPanel content, GridBagConstraints layout) { contentPanel.add(content, layout); } - private void buildCustomizeElement(JPanel panel, JButton button, JTextPane textPane, Theme theme, Color color, - String name, int yIndex) { + private void buildCustomizeElement(JPanel panel, JButton button, JTextPane textPane, Theme theme, Color color, String name, int yIndex) { textPane.setFont(new Font("Arial", Font.PLAIN, 14)); textPane.setBackground(theme.getBackgroundColor()); textPane.setForeground(theme.getUserNameColor()); diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index f0cf53e..b9d08c4 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -9,7 +9,6 @@ import javax.swing.JOptionPane; import envoy.client.Client; import envoy.client.Config; import envoy.client.LocalDB; -import envoy.client.Settings; import envoy.exception.EnvoyException; /** @@ -43,7 +42,8 @@ public class Startup { // Check if all configuration values have been initialized if (!config.isInitialized()) throw new EnvoyException("Server or port are not defined"); } catch (Exception e) { - JOptionPane.showMessageDialog(null, "Error loading configuration values: \n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); + JOptionPane + .showMessageDialog(null, "Error loading configuration values: \n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); System.exit(1); e.printStackTrace(); } @@ -56,7 +56,7 @@ public class Startup { } // Acquire the client user (with ID) either from the server or from the local - // cache (Preferences), which triggers offline mode + // database, which triggers offline mode Client client; try { client = new Client(config, userName); @@ -79,7 +79,6 @@ public class Startup { "Local DB error", JOptionPane.WARNING_MESSAGE); } - Settings.getInstance().setUsername(userName); EventQueue.invokeLater(() -> { try { From 2eb9c79106b4b66a03e1644ecb8f2b1b67873fd5 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sat, 14 Dec 2019 10:53:20 +0100 Subject: [PATCH 5/7] 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); From fb3b53efd8e5ed698729c379d9bc73fef22aed6b Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sat, 14 Dec 2019 11:02:01 +0100 Subject: [PATCH 6/7] Not starting the sync thread in offline mode --- src/main/java/envoy/client/ui/ChatWindow.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 0c85abb..ab0cbb7 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -238,7 +238,9 @@ public class ChatWindow extends JFrame { contentPane.revalidate(); loadUsersAndChats(); - startSyncThread(Config.getInstance().getSyncTimeout()); + + if(client.isOnline()) + startSyncThread(Config.getInstance().getSyncTimeout()); contentPane.revalidate(); } From f044c5cc8d6a98e0a9749267760cc28650e6e842 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sat, 14 Dec 2019 11:30:00 +0100 Subject: [PATCH 7/7] Added documentation, refined exception handling --- src/main/java/envoy/client/Client.java | 35 ++++++++++--- src/main/java/envoy/client/LocalDB.java | 49 +++++++++++-------- src/main/java/envoy/client/ui/ChatWindow.java | 11 +++-- src/main/java/envoy/client/ui/Startup.java | 2 +- 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index 356dc24..b7cc83c 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -33,8 +33,23 @@ public class Client { private User sender, recipient; private boolean online = false; + /** + * Initializes the client. At this state, the client user has yet to be + * initialized, which can be done by calling {@link Client#onlineInit(String). + * + * @param config The {@link Config} instance to use in this client + * @since Envoy v0.2-alpha + */ public Client(Config config) { this.config = config; } + /** + * Enters the online mode by acquiring a user ID from the server. + * + * @param userName the name of the client user + * @throws EnvoyException if the online mode could not be entered or the request + * failed for some other reason + * @since Envoy v0.2-alpha + */ public void onlineInit(String userName) throws EnvoyException { sender = getUser(userName); online = true; @@ -52,10 +67,8 @@ public class Client { } /** - * 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. + * @return a {@code Map} of all users on the server with their + * user names as keys * @since Envoy v0.2-alpha */ public Map getUsers() { @@ -138,9 +151,12 @@ public class Client { * @param sync the sync object (yet to be converted from java class to * sync.xml) * @return a returnSync.xml file + * @throws EnvoyException if the client is not in online mode * @since Envoy v0.1-alpha */ - public Sync sendSync(long userId, Sync sync) { + public Sync sendSync(long userId, Sync sync) throws EnvoyException { + if(!isOnline()) + throw new EnvoyException("Client is not in online mode"); // Print sync XML to console JAXBContext jc; try { @@ -162,6 +178,12 @@ public class Client { */ public User getSender() { return sender; } + /** + * Sets the client user which is used to send messages. + * + * @param sender the client user to set + * @since Envoy v0.2-alpha + */ public void setSender(User sender) { this.sender = sender; } /** @@ -173,7 +195,7 @@ public class Client { /** * Sets the recipient. * - * @param recipient - the recipient to set + * @param recipient the recipient to set * @since Envoy v0.1-alpha */ public void setRecipient(User recipient) { this.recipient = recipient; } @@ -186,6 +208,7 @@ public class Client { /** * @return {@code true} if a connection to the server could be established + * @since Envoy v0.2-alpha */ public boolean isOnline() { return online; } } diff --git a/src/main/java/envoy/client/LocalDB.java b/src/main/java/envoy/client/LocalDB.java index f22c7c7..60de119 100644 --- a/src/main/java/envoy/client/LocalDB.java +++ b/src/main/java/envoy/client/LocalDB.java @@ -36,10 +36,10 @@ import envoy.schema.User; */ public class LocalDB { - private File localDBDir, localDBFile, usersFile; - private User user; + private File localDBDir, localDBFile, usersFile; + private User user; private Map users = new HashMap<>(); - private List chats = new ArrayList<>(); + private List chats = new ArrayList<>(); private ObjectFactory objectFactory = new ObjectFactory(); private DatatypeFactory datatypeFactory; @@ -54,38 +54,38 @@ public class LocalDB { * Constructs an empty local database. To serialize any chats to the file * system, call {@link LocalDB#initializeDBFile(File)}. * + * @param localDBDir the directory in which to store users and chats * @since Envoy v0.1-alpha */ 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"); + usersFile = new File(localDBDir, "users.db"); } /** - * Initializes the local database and fills it with values - * if the user has already sent or received messages. + * Creates a database file for a user-specific list of chats. * - * @param localDBDir the directory where we wish to save/load the database from. - * @throws EnvoyException if the directory selected is not an actual directory. + * @throws NullPointerException if the client user is not yet specified * @since Envoy v0.1-alpha */ - public void initializeDBFile() throws EnvoyException { + public void initializeDBFile() { if (user == null) throw new NullPointerException("Client user is null"); - localDBFile = new File(localDBDir, user.getID() + ".db"); + localDBFile = new File(localDBDir, user.getID() + ".db"); } /** - * Saves the database into a file for future use. + * Stores all users to the local database. If the client user is specified, the + * chats related to this user are stored as well. * * @throws IOException if something went wrong during saving * @since Envoy v0.1-alpha @@ -98,30 +98,36 @@ public class LocalDB { write(localDBFile, chats); } + /** + * Loads all users that are stored in the local database. + * + * @throws EnvoyException if the loading process failed + * @since Envoy v0.2-alpha + */ @SuppressWarnings("unchecked") - public void loadUsers() throws ClassNotFoundException, IOException { users = read(usersFile, HashMap.class); } + public void loadUsers() throws EnvoyException { users = read(usersFile, HashMap.class); } /** * Loads all chats saved by Envoy for the client user. * - * @throws EnvoyException if something fails while loading. - * @throws IOException - * @throws ClassNotFoundException + * @throws EnvoyException if the loading process failed * @since Envoy v0.1-alpha */ @SuppressWarnings("unchecked") - public void loadChats() throws ClassNotFoundException, IOException { chats = read(localDBFile, ArrayList.class); } + public void loadChats() throws EnvoyException { chats = read(localDBFile, ArrayList.class); } - private T read(File file, Class serializedClass) throws ClassNotFoundException, IOException { + private T read(File file, Class serializedClass) throws EnvoyException { + if (file == null) throw new NullPointerException("File is null"); try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) { return serializedClass.cast(in.readObject()); } catch (ClassNotFoundException | IOException e) { - throw e; + throw new EnvoyException("Could not load serialized object", e); } } private void write(File file, T obj) throws IOException { if (file == null) throw new NullPointerException("File is null"); + if (obj == null) throw new NullPointerException("Object to serialize is null"); if (!file.exists()) { file.getParentFile().mkdirs(); file.createNewFile(); @@ -306,7 +312,8 @@ public class LocalDB { public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); } /** - * @return the users + * @return a {@code Map} of all users stored locally with their user names as keys + * @since Envoy v0.2-alpha */ public Map getUsers() { return users; } diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index ab0cbb7..45d607b 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -238,9 +238,8 @@ public class ChatWindow extends JFrame { contentPane.revalidate(); loadUsersAndChats(); - - if(client.isOnline()) - startSyncThread(Config.getInstance().getSyncTimeout()); + + if (client.isOnline()) startSyncThread(Config.getInstance().getSyncTimeout()); contentPane.revalidate(); } @@ -342,7 +341,11 @@ public class ChatWindow extends JFrame { new Thread(() -> { // Synchronize - localDB.applySync(client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID()))); + try { + localDB.applySync(client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID()))); + } catch (Exception e) { + logger.log(Level.SEVERE, "Could not perform sync", e); + } // Process unread messages localDB.addUnreadMessagesToLocalDB(); diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index e2f6b5e..933890d 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -97,7 +97,7 @@ public class Startup { try { localDB.initializeDBFile(); localDB.loadChats(); - } catch (EnvoyException | ClassNotFoundException | IOException e) { + } catch (EnvoyException e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "Error while loading local database: " + e.toString() + "\nChats will not be stored locally.",