Implemented message ID generation and generator requests

* Persisting IdGenerator in LocalDB
* Added Client#requestIdGenerator()
* Listening to MessageCreationEvents in ChatWindow
This commit is contained in:
Kai S. K. Engelbart 2020-01-29 07:44:25 +01:00
parent cd5030735a
commit c0f4e5d270
5 changed files with 100 additions and 18 deletions

View File

@ -28,7 +28,7 @@
<dependency>
<groupId>com.github.informatik-ag-ngl</groupId>
<artifactId>envoy-common</artifactId>
<version>e5c67b8</version>
<version>develop-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@ -11,6 +11,7 @@ import javax.naming.TimeLimitExceededException;
import envoy.client.util.EnvoyLog;
import envoy.data.*;
import envoy.event.IdGeneratorRequest;
import envoy.util.SerializationUtils;
/**
@ -45,11 +46,13 @@ public class Client implements Closeable {
* an exception is thrown.
*
* @param credentials the login credentials of the user
* @param localDB the local database used to persist the current
* {@link IdGenerator}
* @throws Exception if the online mode could not be entered or the request
* failed for some other reason
* @since Envoy v0.2-alpha
*/
public void onlineInit(LoginCredentials credentials) throws Exception {
public void 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());
@ -84,21 +87,38 @@ public class Client implements Closeable {
// Register processors for message and status handling
receiver.registerProcessor(Message.class, new ReceivedMessageProcessor());
// TODO: Status handling
// Process message ID generation
receiver.registerProcessor(IdGenerator.class, localDB::setIdGenerator);
// Request a generator if none is present
if (!localDB.hasIdGenerator() || !localDB.getIdGenerator().hasNext()) requestIdGenerator();
}
/**
* Sends a message to the server.
*
*
* @param message the message to send
* @throws IOException if the message does not reach the server
* @since Envoy v0.3-alpha
*/
public void sendMessage(Message message) throws IOException {
checkOnline();
SerializationUtils.writeBytesWithLength(message, socket.getOutputStream());
writeObject(message);
message.nextStatus();
}
/**
* Requests a new {@link IdGenerator} from the server.
*
* @throws IOException if the request does not reach the server
* @since Envoy v0.3-alpha
*/
public void requestIdGenerator() throws IOException {
logger.info("Requesting new id generator...");
writeObject(new IdGeneratorRequest());
}
/**
* @return a {@code Map<String, User>} of all users on the server with their
* user names as keys
@ -114,6 +134,11 @@ public class Client implements Closeable {
@Override
public void close() throws IOException { if (online) socket.close(); }
private void writeObject(Object obj) throws IOException {
checkOnline();
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
}
private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); }
/**

View File

@ -4,10 +4,15 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
import envoy.data.IdGenerator;
import envoy.data.User;
import envoy.util.SerializationUtils;
/**
* Stored information about the current {@link User} and their {@link Chat}s.
* For message ID generation a {@link IdGenerator} is stored as well.
* These object are persisted inside a folder of the local file system.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>LocalDB.java</strong><br>
* Created: <strong>27.10.2019</strong><br>
@ -18,10 +23,11 @@ import envoy.util.SerializationUtils;
*/
public class LocalDB {
private File localDBDir, localDBFile, usersFile;
private File localDBDir, localDBFile, usersFile, idGeneratorFile;
private User user;
private Map<String, User> users = new HashMap<>();
private List<Chat> chats = new ArrayList<>();
private IdGenerator idGenerator;
/**
* Constructs an empty local database. To serialize any chats to the file
@ -37,7 +43,8 @@ public class LocalDB {
// 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");
idGeneratorFile = new File(localDBDir, "id_generator.db");
}
/**
@ -53,7 +60,8 @@ public class LocalDB {
/**
* Stores all users to the local database. If the client user is specified, the
* chats related to this user are stored as well.
* chats related to this user are stored as well. The message id generator will
* also be saved if present.
*
* @throws IOException if something went wrong during saving
* @since Envoy v0.1-alpha
@ -63,12 +71,15 @@ public class LocalDB {
SerializationUtils.write(usersFile, users);
// Save chats
SerializationUtils.write(localDBFile, chats);
if (user != null) SerializationUtils.write(localDBFile, chats);
// Save id generator
if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator);
}
/**
* Loads all users that are stored in the local database.
*
*
* @throws IOException if the loading process failed
* @throws ClassNotFoundException if the loading process failed
* @since Envoy v0.2-alpha
@ -77,13 +88,25 @@ public class LocalDB {
/**
* Loads all chats saved by Envoy for the client user.
*
*
* @throws IOException if the loading process failed
* @throws ClassNotFoundException if the loading process failed
* @since Envoy v0.1-alpha
*/
public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); }
/**
* Loads the message ID generator that is stored in the local database. If the
* file is not found, the exception is ignored.
*
* @since Envoy v0.3-alpha
*/
public void loadIdGenerator() {
try {
idGenerator = SerializationUtils.read(idGeneratorFile, IdGenerator.class);
} catch (ClassNotFoundException | IOException e) {}
}
/**
* @return a {@code Map<String, User>} of all users stored locally with their
* user names as keys
@ -119,4 +142,22 @@ public class LocalDB {
* @since Envoy v0.2-alpha
*/
public void setUser(User user) { this.user = user; }
/**
* @return the message ID generator
* @since Envoy v0.3-alpha
*/
public IdGenerator getIdGenerator() { return idGenerator; }
/**
* @param idGenerator the message ID generator to set
* @since Envoy v0.3-alpha
*/
public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; }
/**
* @return {@code true} if an {@link IdGenerator} is present
* @since Envoy v0.3-alpha
*/
public boolean hasIdGenerator() { return idGenerator != null; }
}

View File

@ -10,6 +10,7 @@ import javax.swing.*;
import javax.swing.border.EmptyBorder;
import envoy.client.*;
import envoy.client.event.MessageCreationEvent;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.settings.SettingsScreen;
@ -175,7 +176,9 @@ public class ChatWindow extends JFrame {
messageList.setModel(currentChat.getModel());
scrollPane.setChatOpened(true);
contentPane.revalidate();
revalidate();
repaint();
}
});
@ -194,13 +197,20 @@ public class ChatWindow extends JFrame {
contentPane.add(userList, gbc_userList);
contentPane.revalidate();
// Listen to theme changes
EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme((Theme) evt.get()));
// Listen to received messages
EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> {
Message message = ((MessageCreationEvent) evt).get();
localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getRecipientId()).findFirst().get().appendMessage(message);
});
contentPane.revalidate();
}
/**
* Used to immediately reload the ChatWindow when settings were changed.
* Used to immediately reload the {@link ChatWindow} when settings were changed.
*
* @param theme the theme to change colors into
* @since Envoy v0.2-alpha
@ -247,7 +257,7 @@ public class ChatWindow extends JFrame {
if (!messageEnterTextArea.getText().isEmpty()) try {
// Create message
final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId())
final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId(), localDB.getIdGenerator())
.setText(messageEnterTextArea.getText())
.build();
@ -262,12 +272,17 @@ public class ChatWindow extends JFrame {
// Clear text field
messageEnterTextArea.setText("");
// Update UI
revalidate();
repaint();
// Request a new id generator if all ids were used
if (!localDB.getIdGenerator().hasNext()) client.requestIdGenerator();
} catch (Exception e) {
JOptionPane.showMessageDialog(this,
"An exception occured while sending a message. See the log for more details.",
"Exception occured",
"Error sending message:\n" + e.toString(),
"Message sending error",
JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}

View File

@ -62,7 +62,7 @@ public class Startup {
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);
.showMessageDialog(null, "Error loading configuration values:\n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
e.printStackTrace();
}
@ -97,7 +97,8 @@ public class Startup {
Client client = new Client();
try {
// Try entering online mode first
client.onlineInit(credentials);
localDB.loadIdGenerator();
client.onlineInit(credentials, localDB);
} catch (Exception e1) {
logger.warning("Could not connect to server. Trying offline mode...");
e1.printStackTrace();