Added Client#sendMessage(Message), closing socket on exit

This commit is contained in:
Kai S. K. Engelbart 2019-12-31 11:57:11 +02:00
parent 3cebdc8997
commit 46d9cd49f4
5 changed files with 81 additions and 44 deletions

View File

@ -1,11 +1,14 @@
package envoy.client;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.logging.Logger;
import envoy.client.util.EnvoyLog;
import envoy.data.LoginCredentials;
import envoy.data.Message;
import envoy.data.User;
import envoy.exception.EnvoyException;
import envoy.util.SerializationUtils;
@ -20,13 +23,16 @@ import envoy.util.SerializationUtils;
* @author Leon Hofmeister
* @since Envoy v0.1-alpha
*/
public class Client {
public class Client implements Closeable {
private Socket socket;
private Receiver receiver;
private boolean online;
private Socket socket;
private Config config = Config.getInstance();
private volatile User sender;
private User recipient;
private boolean online;
private Config config = Config.getInstance();
private static final Logger logger = EnvoyLog.getLogger(Client.class.getSimpleName());
@ -39,20 +45,21 @@ public class Client {
* @since Envoy v0.2-alpha
*/
public void onlineInit(LoginCredentials credentials) 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());
logger.info("Successfully connected to server.");
// Write login credentials
logger.finest("Sending login credentials...");
SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream());
// Create message receiver
Receiver receiver = new Receiver(socket.getInputStream());
receiver = new Receiver(socket.getInputStream());
// Register user creation processor
receiver.registerProcessor(User.class, sender -> { logger.info("Acquired user object " + sender); this.sender = sender; });
// Write login credentials
logger.finest("Sending login credentials...");
SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream());
// Start receiver
new Thread(receiver).start();
@ -66,6 +73,18 @@ public class Client {
online = true;
}
/**
* 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 {
if (!online) throw new IllegalStateException("Client is not online");
SerializationUtils.writeBytesWithLength(message, socket.getOutputStream());
}
/**
* @return a {@code Map<String, User>} of all users on the server with their
* user names as keys
@ -76,6 +95,9 @@ public class Client {
return null;
}
@Override
public void close() throws IOException { if (online) socket.close(); }
/**
* @return the sender object that represents this client.
* @since Envoy v0.1-alpha
@ -110,6 +132,11 @@ public class Client {
*/
public boolean hasRecipient() { return recipient != null; }
/**
* @return the {@link Receiver} used by this {@link Client}
*/
public Receiver getReceiver() { return receiver; }
/**
* @return {@code true} if a connection to the server could be established
* @since Envoy v0.2-alpha

View File

@ -1,15 +0,0 @@
package envoy.client;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>ObjectProcessor.java</strong><br>
* Created: <strong>30.12.2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
*/
public interface ObjectProcessor<T> {
void process(T input);
}

View File

@ -4,6 +4,7 @@ import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -19,11 +20,16 @@ import envoy.client.util.EnvoyLog;
*/
public class Receiver implements Runnable {
private InputStream in;
private Map<Class<?>, ObjectProcessor<?>> processors = new HashMap<>();
private InputStream in;
private Map<Class<?>, Consumer<?>> processors = new HashMap<>();
private static final Logger logger = EnvoyLog.getLogger(Receiver.class.getSimpleName());
/**
* Creates an instance of {@link Receiver}.
*
* @param in the {@link InputStream} to parse objects from
*/
public Receiver(InputStream in) { this.in = in; }
@SuppressWarnings("unchecked")
@ -35,16 +41,23 @@ public class Receiver implements Runnable {
logger.finest("Received object " + obj);
// Get appropriate processor
ObjectProcessor processor = processors.get(obj.getClass());
@SuppressWarnings("rawtypes")
Consumer processor = processors.get(obj.getClass());
if (processor == null)
logger.severe(String.format("The received object has the class %s for which no processor is defined.", obj.getClass()));
else
processor.process(obj);
else processor.accept(obj);
}
} catch(Exception e) {
} catch (Exception e) {
logger.log(Level.SEVERE, "Error on receiver thread", e);
}
}
public <T> void registerProcessor(Class<T> processorClass, ObjectProcessor<T> processor) { processors.put(processorClass, processor); }
/**
* Adds an object processor to this {@link Receiver}. It will be called once an
* object of the accepted class has been received.
*
* @param processorClass the object class accepted by the processor
* @param processor the object processor
*/
public <T> void registerProcessor(Class<T> processorClass, Consumer<T> processor) { processors.put(processorClass, processor); }
}

View File

@ -246,8 +246,13 @@ public class ChatWindow extends JFrame {
if (!messageEnterTextArea.getText().isEmpty()) try {
// Create and send message object
// Create message
final Message message = new MessageBuilder(localDB.getUser(), currentChat.getRecipient()).setText(messageEnterTextArea.getText()).build();
// Send message
client.sendMessage(message);
// Add message to LocalDB and update UI
currentChat.appendMessage(message);
messageList.setModel(currentChat.getModel());

View File

@ -2,6 +2,7 @@ package envoy.client.ui;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -125,17 +126,17 @@ public class Startup {
// Initialize chats in local database
try {
localDB.initializeDBFile();
// TODO: localDB.loadChats();
localDB.loadChats();
} catch (FileNotFoundException e) {
// The local database file has not yet been created, probably first login
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null,
"Error while loading local database: " + e.toString() + "\nChats will not be stored locally.",
"Error while loading local database: " + e.toString() + "\nChats might 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());
@ -147,8 +148,13 @@ public class Startup {
try {
new StatusTrayIcon(chatWindow).show();
// If the tray icon is supported and corresponding settings is set, hide the chat window on close
Settings.getInstance().getItems().get("onCloseMode").setChangeHandler((onCloseMode) -> chatWindow.setDefaultCloseOperation((boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE));
// If the tray icon is supported and corresponding settings is set, hide the
// chat window on close
Settings.getInstance()
.getItems()
.get("onCloseMode")
.setChangeHandler((onCloseMode) -> chatWindow
.setDefaultCloseOperation((boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE));
} catch (EnvoyException e) {
logger.warning("The StatusTrayIcon is not supported on this platform!");
}
@ -160,13 +166,14 @@ public class Startup {
// Save Settings and LocalDB on shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
logger.info("Saving local database...");
logger.info("Closing connection...");
client.close();
logger.info("Saving local database and settings...");
localDB.save();
logger.info("Saving settings...");
Settings.getInstance().save();
} catch (IOException e1) {
e1.printStackTrace();
logger.log(Level.WARNING, "Unable to save the messages", e1);
} catch (IOException e) {
logger.log(Level.SEVERE, "Unable to save local files", e);
}
}));
}