From 1b60ab3f0d3940877fea9d5d28c07484de8a9d64 Mon Sep 17 00:00:00 2001 From: delvh Date: Tue, 22 Sep 2020 14:42:51 +0200 Subject: [PATCH] =?UTF-8?q?Fixed=20Bug=20Not=20Saving=20Values=20When=20Ex?= =?UTF-8?q?iting=20via=20=E2=80=9CControl=E2=80=9D+=E2=80=9DQ=E2=80=9D=20(?= =?UTF-8?q?#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed bug not saving values when exiting via "Control"+"Q" Reviewed-on: https://git.kske.dev/zdm/envoy/pulls/40 Reviewed-by: kske --- .../main/java/envoy/client/data/LocalDB.java | 52 +++++++++++-------- .../main/java/envoy/client/data/Settings.java | 21 ++++++-- .../envoy/client/event/EnvoyCloseEvent.java | 16 ++++++ .../main/java/envoy/client/net/Client.java | 16 ++++-- .../main/java/envoy/client/net/Receiver.java | 11 ++-- .../java/envoy/client/ui/SceneContext.java | 7 ++- .../main/java/envoy/client/ui/Startup.java | 28 ++-------- 7 files changed, 87 insertions(+), 64 deletions(-) create mode 100644 client/src/main/java/envoy/client/event/EnvoyCloseEvent.java diff --git a/client/src/main/java/envoy/client/data/LocalDB.java b/client/src/main/java/envoy/client/data/LocalDB.java index faee4fd..add6962 100644 --- a/client/src/main/java/envoy/client/data/LocalDB.java +++ b/client/src/main/java/envoy/client/data/LocalDB.java @@ -5,11 +5,13 @@ import java.nio.channels.*; import java.nio.file.StandardOpenOption; import java.time.Instant; import java.util.*; +import java.util.logging.Level; +import envoy.client.event.EnvoyCloseEvent; import envoy.data.*; import envoy.event.*; import envoy.exception.EnvoyException; -import envoy.util.SerializationUtils; +import envoy.util.*; import dev.kske.eventbus.Event; import dev.kske.eventbus.EventBus; @@ -42,12 +44,13 @@ public final class LocalDB implements EventListener { private Instant lastSync = Instant.EPOCH; // Persistence - private File dbDir, userFile, idGeneratorFile, lastLoginFile, usersFile; + private File userFile; private FileLock instanceLock; + private final File dbDir, idGeneratorFile, lastLoginFile, usersFile; + /** - * Constructs an empty local database. To serialize any user-specific data to - * the file system, call {@link LocalDB#save(boolean)}. + * Constructs an empty local database. * * @param dbDir the directory in which to persist data * @throws IOException if {@code dbDir} is a file (and not a directory) @@ -59,9 +62,8 @@ public final class LocalDB implements EventListener { EventBus.getInstance().registerListener(this); // Ensure that the database directory exists - if (!dbDir.exists()) { - dbDir.mkdirs(); - } else if (!dbDir.isDirectory()) throw new IOException(String.format("LocalDBDir '%s' is not a directory!", dbDir.getAbsolutePath())); + if (!dbDir.exists()) dbDir.mkdirs(); + else if (!dbDir.isDirectory()) throw new IOException(String.format("LocalDBDir '%s' is not a directory!", dbDir.getAbsolutePath())); // Lock the directory lock(); @@ -88,12 +90,12 @@ public final class LocalDB implements EventListener { * @since Envoy Client v0.2-beta */ private synchronized void lock() throws EnvoyException { - File file = new File(dbDir, "instance.lock"); + final var file = new File(dbDir, "instance.lock"); try { - FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); + final var fc = FileChannel.open(file.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); instanceLock = fc.tryLock(); if (instanceLock == null) throw new EnvoyException("Another Envoy instance is using this local database!"); - } catch (IOException e) { + } catch (final IOException e) { throw new EnvoyException("Could not create lock file!", e); } } @@ -146,9 +148,8 @@ public final class LocalDB implements EventListener { users.put(user.getName(), user); // Synchronize user status data - for (Contact contact : users.values()) - if (contact instanceof User) - getChat(contact.getID()).ifPresent(chat -> { ((User) chat.getRecipient()).setStatus(((User) contact).getStatus()); }); + for (final var contact : users.values()) + if (contact instanceof User) getChat(contact.getID()).ifPresent(chat -> { ((User) chat.getRecipient()).setStatus(contact.getStatus()); }); // Create missing chats user.getContacts() @@ -162,26 +163,31 @@ public final class LocalDB implements EventListener { * Stores all users. If the client user is specified, their chats will be stored * as well. The message id generator will also be saved if present. * - * @param isOnline determines which {@code lastSync} time stamp is saved * @throws IOException if the saving process failed * @since Envoy Client v0.3-alpha */ - public synchronized void save(boolean isOnline) throws IOException { + @Event(eventType = EnvoyCloseEvent.class, priority = 1000) + private synchronized void save() { + EnvoyLog.getLogger(LocalDB.class).log(Level.INFO, "Saving local database..."); // Save users - SerializationUtils.write(usersFile, users); + try { + SerializationUtils.write(usersFile, users); - // Save user data and last sync time stamp - if (user != null) SerializationUtils.write(userFile, chats, cacheMap, isOnline ? Instant.now() : lastSync); + // Save user data and last sync time stamp + if (user != null) + SerializationUtils.write(userFile, chats, cacheMap, Context.getInstance().getClient().isOnline() ? Instant.now() : lastSync); - // Save last login information - if (authToken != null) SerializationUtils.write(lastLoginFile, user, authToken); + // Save last login information + if (authToken != null) SerializationUtils.write(lastLoginFile, user, authToken); - // Save id generator - if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); + // Save ID generator + if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); + } catch (final IOException e) { + EnvoyLog.getLogger(LocalDB.class).log(Level.SEVERE, "Unable to save local database: ", e); + } } - @Event private void onNewAuthToken(NewAuthToken evt) { authToken = evt.get(); } diff --git a/client/src/main/java/envoy/client/data/Settings.java b/client/src/main/java/envoy/client/data/Settings.java index 24505ff..b75f165 100644 --- a/client/src/main/java/envoy/client/data/Settings.java +++ b/client/src/main/java/envoy/client/data/Settings.java @@ -2,9 +2,14 @@ package envoy.client.data; import java.io.*; import java.util.*; +import java.util.logging.Level; import java.util.prefs.Preferences; -import envoy.util.SerializationUtils; +import envoy.client.event.EnvoyCloseEvent; +import envoy.util.*; + +import dev.kske.eventbus.*; +import dev.kske.eventbus.EventListener; /** * Manages all application settings, which are different objects that can be @@ -20,7 +25,7 @@ import envoy.util.SerializationUtils; * @author Kai S. K. Engelbart * @since Envoy Client v0.2-alpha */ -public final class Settings { +public final class Settings implements EventListener { // Actual settings accessible by the rest of the application private Map> items; @@ -42,6 +47,8 @@ public final class Settings { * @since Envoy Client v0.2-alpha */ private Settings() { + EventBus.getInstance().registerListener(this); + // Load settings from settings file try { items = SerializationUtils.read(settingsFile, HashMap.class); @@ -65,10 +72,16 @@ public final class Settings { * @throws IOException if an error occurs while saving the themes * @since Envoy Client v0.2-alpha */ - public void save() throws IOException { + @Event(eventType = EnvoyCloseEvent.class, priority = 900) + private void save() { + EnvoyLog.getLogger(Settings.class).log(Level.INFO, "Saving settings..."); // Save settings to settings file - SerializationUtils.write(settingsFile, items); + try { + SerializationUtils.write(settingsFile, items); + } catch (final IOException e) { + EnvoyLog.getLogger(Settings.class).log(Level.SEVERE, "Unable to save settings file: ", e); + } } private void supplementDefaults() { diff --git a/client/src/main/java/envoy/client/event/EnvoyCloseEvent.java b/client/src/main/java/envoy/client/event/EnvoyCloseEvent.java new file mode 100644 index 0000000..cb9be17 --- /dev/null +++ b/client/src/main/java/envoy/client/event/EnvoyCloseEvent.java @@ -0,0 +1,16 @@ +package envoy.client.event; + +import envoy.event.Event.Valueless; + +/** + * This event will be sent once Envoy is really closed. + * Its purpose is to forcefully stop other threads peacefully so that the VM can + * shutdown too. + * + * @author Leon Hofmeister + * @since Envoy Client v0.2-beta + */ +public class EnvoyCloseEvent extends Valueless { + + private static final long serialVersionUID = 1L; +} diff --git a/client/src/main/java/envoy/client/net/Client.java b/client/src/main/java/envoy/client/net/Client.java index 9e41343..f2987b3 100644 --- a/client/src/main/java/envoy/client/net/Client.java +++ b/client/src/main/java/envoy/client/net/Client.java @@ -6,7 +6,7 @@ import java.util.concurrent.TimeoutException; import java.util.logging.*; import envoy.client.data.*; -import envoy.client.event.SendEvent; +import envoy.client.event.*; import envoy.data.*; import envoy.event.*; import envoy.event.Event; @@ -49,9 +49,7 @@ public final class Client implements EventListener, Closeable { * * @since Envoy Client v0.2-beta */ - public Client() { - eventBus.registerListener(this); - } + public Client() { eventBus.registerListener(this); } /** * Enters the online mode by acquiring a user ID from the server. As a @@ -236,7 +234,15 @@ public final class Client implements EventListener, Closeable { } @Override - public void close() throws IOException { if (online) socket.close(); } + @dev.kske.eventbus.Event(eventType = EnvoyCloseEvent.class, priority = 800) + public void close() { + if (online) { + logger.log(Level.INFO, "Closing connection..."); + try { + socket.close(); + } catch (final IOException e) {} + } + } private void writeObject(Object obj) throws IOException { checkOnline(); diff --git a/client/src/main/java/envoy/client/net/Receiver.java b/client/src/main/java/envoy/client/net/Receiver.java index e9ac48f..ac806ad 100644 --- a/client/src/main/java/envoy/client/net/Receiver.java +++ b/client/src/main/java/envoy/client/net/Receiver.java @@ -2,14 +2,11 @@ package envoy.client.net; import java.io.*; import java.net.SocketException; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.logging.*; -import envoy.util.EnvoyLog; -import envoy.util.SerializationUtils; +import envoy.util.*; /** * Receives objects from the server and passes them to processor objects based @@ -90,7 +87,7 @@ public final class Receiver extends Thread { } } catch (final SocketException | EOFException e) { // Connection probably closed by client. - logger.log(Level.FINER, "Exiting receiver..."); + logger.log(Level.INFO, "Exiting receiver..."); return; } catch (final Exception e) { logger.log(Level.SEVERE, "Error on receiver thread", e); diff --git a/client/src/main/java/envoy/client/ui/SceneContext.java b/client/src/main/java/envoy/client/ui/SceneContext.java index 083333a..f26aaef 100644 --- a/client/src/main/java/envoy/client/ui/SceneContext.java +++ b/client/src/main/java/envoy/client/ui/SceneContext.java @@ -11,7 +11,7 @@ import javafx.scene.input.*; import javafx.stage.Stage; import envoy.client.data.Settings; -import envoy.client.event.ThemeChangeEvent; +import envoy.client.event.*; import envoy.util.EnvoyLog; import dev.kske.eventbus.*; @@ -114,7 +114,10 @@ public final class SceneContext implements EventListener { // Presumably no Settings are loaded in the login scene, hence Envoy is closed // directly if (sceneInfo != SceneInfo.LOGIN_SCENE && settings.isHideOnClose()) stage.setIconified(true); - else System.exit(0); + else { + EventBus.getInstance().dispatch(new EnvoyCloseEvent()); + System.exit(0); + } }); // The LoginScene is the only scene not intended to be resized diff --git a/client/src/main/java/envoy/client/ui/Startup.java b/client/src/main/java/envoy/client/ui/Startup.java index 15d03f3..84bbba4 100644 --- a/client/src/main/java/envoy/client/ui/Startup.java +++ b/client/src/main/java/envoy/client/ui/Startup.java @@ -11,6 +11,7 @@ import javafx.scene.control.Alert.AlertType; import javafx.stage.Stage; import envoy.client.data.*; +import envoy.client.event.EnvoyCloseEvent; import envoy.client.net.Client; import envoy.client.ui.SceneContext.SceneInfo; import envoy.client.ui.controller.LoginScene; @@ -20,6 +21,8 @@ import envoy.event.*; import envoy.exception.EnvoyException; import envoy.util.EnvoyLog; +import dev.kske.eventbus.EventBus; + /** * Handles application startup and shutdown. *

@@ -95,7 +98,7 @@ public final class Startup extends Application { if (!performHandshake( LoginCredentials.loginWithToken(localDB.getUser().getName(), localDB.getAuthToken(), VERSION, localDB.getLastSync()))) sceneContext.load(SceneInfo.LOGIN_SCENE); - } else + } else // Load login scene sceneContext.load(SceneInfo.LOGIN_SCENE); } @@ -213,7 +216,7 @@ public final class Startup extends Application { if (Settings.getInstance().isHideOnClose()) { stage.setIconified(true); e.consume(); - } + } else EventBus.getInstance().dispatch(new EnvoyCloseEvent()); }); // Initialize status tray icon @@ -224,25 +227,4 @@ public final class Startup extends Application { }); } } - - /** - * Closes the client connection and saves the local database and settings. - * - * @since Envoy Client v0.1-beta - */ - @Override - public void stop() { - try { - logger.log(Level.INFO, "Saving local database and settings..."); - localDB.save(client.isOnline()); - Settings.getInstance().save(); - - if (client.isOnline()) logger.log(Level.INFO, "Closing connection..."); - client.close(); - - logger.log(Level.INFO, "Envoy was terminated by its user"); - } catch (final Exception e) { - logger.log(Level.SEVERE, "Unable to save local files: ", e); - } - } }