diff --git a/.classpath b/.classpath index 1a0c560..a5d9509 100644 --- a/.classpath +++ b/.classpath @@ -28,11 +28,5 @@ - - - - - - diff --git a/pom.xml b/pom.xml index d688c37..28e6f60 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ informatik-ag-ngl envoy-client - 0.0.1-SNAPSHOT + 0.3-alpha Envoy Client https://github.com/informatik-ag-ngl/envoy-client @@ -18,20 +18,10 @@ - - org.jboss.resteasy - resteasy-client - 4.1.1.Final - - - org.jboss.resteasy - resteasy-jaxb-provider - 4.3.1.Final - informatik-ag-ngl envoy-common - 0.0.1-SNAPSHOT + 0.2-alpha diff --git a/src/main/java/envoy/client/Chat.java b/src/main/java/envoy/client/Chat.java index daca33a..ef762c4 100644 --- a/src/main/java/envoy/client/Chat.java +++ b/src/main/java/envoy/client/Chat.java @@ -4,8 +4,8 @@ import java.io.Serializable; import javax.swing.DefaultListModel; -import envoy.schema.Message; -import envoy.schema.User; +import envoy.data.Message; +import envoy.data.User; /** * Represents a chat between two {@link User}s
diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index edc4839..4885a92 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -1,23 +1,17 @@ package envoy.client; -import java.io.StringWriter; +import java.io.Closeable; +import java.io.IOException; +import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Response; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; +import javax.naming.TimeLimitExceededException; import envoy.client.util.EnvoyLog; -import envoy.exception.EnvoyException; -import envoy.schema.ObjectFactory; -import envoy.schema.Sync; -import envoy.schema.User; +import envoy.data.*; +import envoy.util.SerializationUtils; /** * Project: envoy-client
@@ -29,46 +23,80 @@ import envoy.schema.User; * @author Leon Hofmeister * @since Envoy v0.1-alpha */ -public class Client { +public class Client implements Closeable { - private ObjectFactory objectFactory = new ObjectFactory(); - private Config config; - private User sender, recipient; - private boolean online = false; + private Socket socket; + private Receiver receiver; + private boolean online; + + private volatile User sender; + private User recipient; + + private volatile Contacts contacts; + + private Config config = Config.getInstance(); private static final Logger logger = EnvoyLog.getLogger(Client.class.getSimpleName()); /** - * Initializes the client. At this state, the client user has yet to be - * initialized, which can be done by calling {@link Client#onlineInit(String)}. + * Enters the online mode by acquiring a user ID from the server. As a + * connection has to be established and a handshake has to be made, this method + * will block for up to 5 seconds. If the handshake does exceed this time limit, + * an exception is thrown. * - * @param config The {@link Config} instance to use in this client + * @param credentials the login credentials of the user + * @throws Exception if the online mode could not be entered or the request + * failed for some other reason * @since Envoy v0.2-alpha */ - public Client(Config config) { this.config = config; } + 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."); - /** - * 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; + // Create message receiver + receiver = new Receiver(socket.getInputStream()); + + // Register user creation processor + receiver.registerProcessor(User.class, sender -> { logger.info("Acquired user object " + sender); this.sender = sender; }); + receiver.registerProcessor(Contacts.class, contacts -> { logger.info("Acquired contacts object " + contacts); this.contacts = contacts; }); + + // Start receiver + new Thread(receiver).start(); + + // Write login credentials + logger.finest("Sending login credentials..."); + SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream()); + + // Wait for a maximum of five seconds to acquire the sender object + long start = System.currentTimeMillis(); + while (sender == null || contacts == null) { + if (System.currentTimeMillis() - start > 5000) throw new TimeLimitExceededException("Did not log in after 5 seconds"); + Thread.sleep(500); + } + + logger.info("Handshake completed."); + online = true; + + // Remove user creation processor + receiver.removeAllProcessors(); + + // Register processors for message and status handling + receiver.registerProcessor(Message.class, new ReceivedMessageProcessor()); } - private R post(String uri, T body, Class responseBodyClass) { - javax.ws.rs.client.Client client = ClientBuilder.newClient(); - WebTarget target = client.target(uri); - Response response = target.request().post(Entity.entity(body, "application/xml")); - R responseBody = response.readEntity(responseBodyClass); - response.close(); - client.close(); - - return responseBody; + /** + * 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()); + message.nextStatus(); } /** @@ -77,107 +105,16 @@ public class Client { * @since Envoy v0.2-alpha */ public Map getUsers() { - Sync sendSync = objectFactory.createSync(); - User user = objectFactory.createUser(); - user.setID(-1); - sendSync.getUsers().add(user); - - Sync returnSync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), - sendSync, - Sync.class); - + checkOnline(); Map users = new HashMap<>(); - returnSync.getUsers().forEach(u -> users.put(u.getName(), u)); + contacts.getContacts().forEach(u -> users.put(u.getName(), u)); return users; } - /** - * Returns a {@link User} with a specific id by name. - * - * @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) 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); + @Override + public void close() throws IOException { if (online) socket.close(); } - try { - Sync sync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), - senderSync, - Sync.class); - - // 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) { - throw new EnvoyException("Could not connect to server", e); - } - } - - /** - * Sends the "sync" Sync Object to the server and gets a "returnSync" Sync - * Object as response.
- * It is also used to get the own sender at the start of the client - * (Client sends "sync" Sync Object with single user in it(name: the name - * entered at login, id: 0, UserStatus:null))
- * and to get a complete list of all users saved on the server. - * (Client sends "sync" Sync Object with single user in it(name: "" (empty), id: - * -1, UserStatus:null))
- * This method also processes the response Sync Object.
- * It sorts its users and messages by specific variables and does certain things - * with them.
- *
- * Messages:
- * -State SENT: Update Local message(s) with State WAITING (add Message ID and - * change State to SENT). (server sends these informations to the client if - * message(s) with State WAITING were successfully sent to the server)
- * -State RECEIVED, SenderID != 0: Adds the unread Messages returned from the - * server in the latest sync to the "unreadMessagesSync" Sync Object.
- * -State RECEIVED, SenderID == 0: Update message(s) in localDB to state - * RECEIVED. - * (server sends these informations to the client if the other client received - * the message(s).)
- * -State READ: Update message(s) in the LocalDB to state READ. (server sends - * these informations to the client if the other client read - * the message(s).)
- *
- * Users:
- * Updating UserStatus of all users in LocalDB. (Server sends all users with - * 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) - * @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) throws EnvoyException { - if(!isOnline()) - throw new EnvoyException("Client is not in online mode"); - // Print sync XML to console - JAXBContext jc; - try { - jc = JAXBContext.newInstance("envoy.schema"); - Marshaller m = jc.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - StringWriter stringWriter = new StringWriter(); - m.marshal(sync, stringWriter); - logger.fine("Sending sync:\n" + stringWriter.toString()); - } catch (JAXBException e) { - e.printStackTrace(); - } - - // Send sync - return post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), userId), sync, Sync.class); - } + private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); } /** * @return the sender object that represents this client. @@ -213,6 +150,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 diff --git a/src/main/java/envoy/client/LocalDB.java b/src/main/java/envoy/client/LocalDB.java index b5e3971..2c40e65 100644 --- a/src/main/java/envoy/client/LocalDB.java +++ b/src/main/java/envoy/client/LocalDB.java @@ -2,20 +2,10 @@ package envoy.client; import java.io.File; import java.io.IOException; -import java.time.Instant; import java.util.*; -import java.util.logging.Logger; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; - -import envoy.client.event.EventBus; -import envoy.client.event.MessageCreationEvent; -import envoy.client.util.EnvoyLog; -import envoy.client.util.SerializationUtils; -import envoy.exception.EnvoyException; -import envoy.schema.*; -import envoy.schema.Message.Metadata.MessageState; +import envoy.data.User; +import envoy.util.SerializationUtils; /** * Project: envoy-client
@@ -33,15 +23,6 @@ public class LocalDB { private Map users = new HashMap<>(); private List chats = new ArrayList<>(); - private ObjectFactory objectFactory = new ObjectFactory(); - private DatatypeFactory datatypeFactory; - - private Sync unreadMessagesSync = objectFactory.createSync(); - private Sync sync = objectFactory.createSync(); - private Sync readMessages = objectFactory.createSync(); - - private static final Logger logger = EnvoyLog.getLogger(LocalDB.class.getSimpleName()); - /** * Constructs an empty local database. To serialize any chats to the file * system, call {@link LocalDB#initializeDBFile()}. @@ -53,12 +34,6 @@ public class 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())); @@ -73,7 +48,7 @@ public class LocalDB { */ 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"); } /** @@ -93,188 +68,21 @@ public class LocalDB { /** * Loads all users that are stored in the local database. - * - * @throws EnvoyException if the loading process failed + * + * @throws IOException if the loading process failed + * @throws ClassNotFoundException if the loading process failed * @since Envoy v0.2-alpha */ - public void loadUsers() throws EnvoyException { users = SerializationUtils.read(usersFile, HashMap.class); } + public void loadUsers() throws ClassNotFoundException, IOException { users = SerializationUtils.read(usersFile, HashMap.class); } /** * Loads all chats saved by Envoy for the client user. - * - * @throws EnvoyException if the loading process failed + * + * @throws IOException if the loading process failed + * @throws ClassNotFoundException if the loading process failed * @since Envoy v0.1-alpha */ - public void loadChats() throws EnvoyException { chats = SerializationUtils.read(localDBFile, ArrayList.class); } - - /** - * Creates a {@link Message} object serializable to XML. - * - * @param textContent The content (text) of the message - * @param recipientID The recipient of the message - * @return prepared {@link Message} object - * @since Envoy v0.1-alpha - */ - public Message createMessage(String textContent, long recipientID) { - Message.Metadata metaData = objectFactory.createMessageMetadata(); - metaData.setSender(user.getID()); - metaData.setRecipient(recipientID); - metaData.setState(MessageState.WAITING); - metaData.setDate(datatypeFactory.newXMLGregorianCalendar(Instant.now().toString())); - - Message.Content content = objectFactory.createMessageContent(); - content.setType("text"); - content.setText(textContent); - - Message message = objectFactory.createMessage(); - message.setMetadata(metaData); - message.getContent().add(content); - - 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 - */ - public Sync fillSync(long userId) { - addWaitingMessagesToSync(); - - sync.getMessages().addAll(readMessages.getMessages()); - readMessages.getMessages().clear(); - - logger.finest(String.format("Filled sync with %d messages.", sync.getMessages().size())); - return sync; - } - - /** - * 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 - */ - public void applySync(Sync returnSync) { - for (int i = 0; i < returnSync.getMessages().size(); i++) { - - // The message has an ID - if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0) { - - // Messages are processes differently corresponding to their state - switch (returnSync.getMessages().get(i).getMetadata().getState()) { - case SENT: - // Update previously waiting and now sent messages that were assigned an ID by - // the server - sync.getMessages().get(i).getMetadata().setMessageId(returnSync.getMessages().get(i).getMetadata().getMessageId()); - sync.getMessages().get(i).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); - break; - case RECEIVED: - if (returnSync.getMessages().get(i).getMetadata().getSender() != 0) { - // these are the unread Messages from the server - unreadMessagesSync.getMessages().add(returnSync.getMessages().get(i)); - - // Create and dispatch message creation event - EventBus.getInstance().dispatch(new MessageCreationEvent(returnSync.getMessages().get(i))); - } else { - // Update Messages in localDB to state RECEIVED - for (Chat chat : getChats()) - if (chat.getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) - for (int j = 0; j < chat.getModel().getSize(); j++) - if (chat.getModel().get(j).getMetadata().getMessageId() == returnSync.getMessages() - .get(i) - .getMetadata() - .getMessageId()) - chat.getModel().get(j).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); - } - break; - case READ: - // Update local Messages to state READ - logger.info("Message with ID: " + returnSync.getMessages().get(i).getMetadata().getMessageId() - + "was initialized to be set to READ in localDB."); - for (Chat chat : getChats()) - if (chat.getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) { - logger.info("Chat with: " + chat.getRecipient().getID() + "was selected."); - for (int k = 0; k < chat.getModel().getSize(); k++) - if (chat.getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages() - .get(i) - .getMetadata() - .getMessageId()) { - logger.info("Message with ID: " + chat.getModel().get(k).getMetadata().getMessageId() + "was selected."); - chat.getModel().get(k).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); - logger.info("Message State is now: " + chat.getModel().get(k).getMetadata().getState()); - } - } - break; - } - } - } - - // Updating UserStatus of all users in LocalDB - for (User user : returnSync.getUsers()) - for (Chat chat : getChats()) - if (user.getID() == chat.getRecipient().getID()) chat.getRecipient().setStatus(user.getStatus()); - - sync.getMessages().clear(); - sync.getUsers().clear(); - } - - /** - * Adds the unread messages returned from the server in the latest sync to the - * right chats in the LocalDB. - * - * @since Envoy v0.1-alpha - */ - public void addUnreadMessagesToLocalDB() { - for (Message message : unreadMessagesSync.getMessages()) - for (Chat chat : getChats()) - if (message.getMetadata().getSender() == chat.getRecipient().getID()) { - chat.appendMessage(message); - break; - } - } - - /** - * Changes all messages with state {@code RECEIVED} of a specific chat to state - * {@code READ}. - *
- * Adds these messages to the {@code readMessages} {@link Sync} object. - * - * @param currentChat the {@link Chat} that was just opened - * @since Envoy v0.1-alpha - */ - public void setMessagesToRead(Chat currentChat) { - for (int i = currentChat.getModel().size() - 1; i >= 0; --i) - if (currentChat.getModel().get(i).getMetadata().getRecipient() != currentChat.getRecipient().getID()) - if (currentChat.getModel().get(i).getMetadata().getState() == MessageState.RECEIVED) { - currentChat.getModel().get(i).getMetadata().setState(MessageState.READ); - readMessages.getMessages().add(currentChat.getModel().get(i)); - } else break; - } - - /** - * Adds all messages with state {@code WAITING} from the {@link LocalDB} to the - * {@link Sync} object. - * - * @since Envoy v0.1-alpha - */ - private void addWaitingMessagesToSync() { - for (Chat chat : getChats()) - for (int i = 0; i < chat.getModel().size(); i++) - if (chat.getModel().get(i).getMetadata().getState() == MessageState.WAITING) { - logger.info("Got Waiting Message"); - sync.getMessages().add(chat.getModel().get(i)); - } - } - - /** - * Clears the {@code unreadMessagesSync} {@link Sync} object. - * - * @since Envoy v0.1-alpha - */ - public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); } + public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); } /** * @return a {@code Map} of all users stored locally with their diff --git a/src/main/java/envoy/client/ReceivedMessageProcessor.java b/src/main/java/envoy/client/ReceivedMessageProcessor.java new file mode 100644 index 0000000..d408993 --- /dev/null +++ b/src/main/java/envoy/client/ReceivedMessageProcessor.java @@ -0,0 +1,32 @@ +package envoy.client; + +import java.util.function.Consumer; +import java.util.logging.Logger; + +import envoy.client.event.MessageCreationEvent; +import envoy.client.util.EnvoyLog; +import envoy.data.Message; +import envoy.data.Message.MessageStatus; +import envoy.event.EventBus; + +/** + * Project: envoy-client
+ * File: ReceivedMessageProcessor.java
+ * Created: 31.12.2019
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class ReceivedMessageProcessor implements Consumer { + + private static final Logger logger = EnvoyLog.getLogger(ReceivedMessageProcessor.class.getSimpleName()); + + @Override + public void accept(Message message) { + logger.info("Received message object " + message); + if (message.getStatus() != MessageStatus.SENT) logger.warning("The message has the unexpected status " + message.getStatus()); + else + // Dispatch event + EventBus.getInstance().dispatch(new MessageCreationEvent(message)); + } +} diff --git a/src/main/java/envoy/client/Receiver.java b/src/main/java/envoy/client/Receiver.java new file mode 100644 index 0000000..e6cd3c5 --- /dev/null +++ b/src/main/java/envoy/client/Receiver.java @@ -0,0 +1,86 @@ +package envoy.client; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.net.SocketException; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import envoy.client.util.EnvoyLog; +import envoy.util.SerializationUtils; + +/** + * Project: envoy-client
+ * File: Receiver.java
+ * Created: 30.12.2019
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class Receiver implements Runnable { + + private final InputStream in; + private final Map, 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") + @Override + public void run() { + + try { + while (true) { + // Read object length + byte[] lenBytes = new byte[4]; + in.read(lenBytes); + int len = SerializationUtils.bytesToInt(lenBytes, 0); + + // Read object into byte array + byte[] objBytes = new byte[len]; + in.read(objBytes); + + try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) { + Object obj = oin.readObject(); + logger.finest("Received object " + obj); + + // Get appropriate processor + @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.accept(obj); + } + } + } catch (SocketException e) { + logger.info("Connection probably closed by client. Exiting receiver thread..."); + } catch (Exception e) { + logger.log(Level.SEVERE, "Error on receiver thread", e); + e.printStackTrace(); + } + } + + /** + * 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 void registerProcessor(Class processorClass, Consumer processor) { processors.put(processorClass, processor); } + + /** + * Removes all object processors registered at this {@link Receiver}. + */ + public void removeAllProcessors() { processors.clear(); } +} \ No newline at end of file diff --git a/src/main/java/envoy/client/Settings.java b/src/main/java/envoy/client/Settings.java index cdecc33..ef451d0 100644 --- a/src/main/java/envoy/client/Settings.java +++ b/src/main/java/envoy/client/Settings.java @@ -1,14 +1,14 @@ package envoy.client; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.prefs.Preferences; import envoy.client.ui.Color; import envoy.client.ui.Theme; -import envoy.client.util.SerializationUtils; -import envoy.exception.EnvoyException; +import envoy.util.SerializationUtils; /** * Manages all application settings, which are different objects that can be @@ -55,7 +55,7 @@ public class Settings { // Load settings from settings file try { items = SerializationUtils.read(settingsFile, HashMap.class); - } catch (EnvoyException e) { + } catch (ClassNotFoundException | IOException e) { items = new HashMap<>(); } supplementDefaults(); @@ -63,7 +63,7 @@ public class Settings { // Load themes from theme file try { themes = SerializationUtils.read(themeFile, HashMap.class); - } catch (EnvoyException e1) { + } catch (ClassNotFoundException | IOException e1) { themes = new HashMap<>(); setCurrentTheme("dark"); } diff --git a/src/main/java/envoy/client/event/Event.java b/src/main/java/envoy/client/event/Event.java deleted file mode 100644 index 8e5393a..0000000 --- a/src/main/java/envoy/client/event/Event.java +++ /dev/null @@ -1,19 +0,0 @@ -package envoy.client.event; - -/** - * Project: envoy-client
- * File: Event.java
- * Created: 04.12.2019
- * - * @author Kai S. K. Engelbart - * @param the type of the Event - * @since Envoy v0.2-alpha - */ -public interface Event { - - /** - * @return the data associated with this event - */ - T get(); -} - diff --git a/src/main/java/envoy/client/event/EventBus.java b/src/main/java/envoy/client/event/EventBus.java deleted file mode 100644 index 06b385f..0000000 --- a/src/main/java/envoy/client/event/EventBus.java +++ /dev/null @@ -1,81 +0,0 @@ -package envoy.client.event; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * This class handles events by allowing {@link EventHandler} object to register - * themselves and then be notified about certain events dispatched by the event - * bus.
- *
- * The event bus is a singleton and can be used across the entire application to - * guarantee the propagation of events.
- * - * Project: envoy-client
- * File: EventBus.java
- * Created: 04.12.2019
- * - * @author Kai S. K. Engelbart - * @since Envoy v0.2-alpha - */ -public class EventBus { - - /** - * Contains all {@link EventHandler} instances registered at this - * {@link EventBus} as values mapped to by their supported {@link Event} - * classes. - */ - private Map>, List> handlers = new HashMap<>(); - - /** - * The singleton instance of this {@link EventBus} that is used across the - * entire application. - */ - private static EventBus eventBus = new EventBus(); - - /** - * This constructor is not accessible from outside this class because a - * singleton instance of it is provided by the {@link EventBus#getInstance()} - * method. - */ - private EventBus() {} - - /** - * @return the singleton instance of the {@link EventBus} - * @since Envoy v0.2-alpha - */ - public static EventBus getInstance() { return eventBus; } - - /** - * Registers an {@link EventHandler} to be notified when a - * {@link Event} of a certain type is dispatched. - * - * @param eventClass the class which the {@link EventHandler} is subscribed to - * @param handler the {@link EventHandler} to register - * @since Envoy v0.2-alpha - */ - public void register(Class> eventClass, EventHandler handler) { - if (!handlers.containsKey(eventClass)) handlers.put(eventClass, new ArrayList<>()); - handlers.get(eventClass).add(handler); - } - - /** - * Dispatches a {@link Event} to every {@link EventHandler} subscribed to it. - * - * @param event the {@link Event} to dispatch - * @since Envoy v0.2-alpha - */ - public void dispatch(Event event) { - handlers.keySet().stream().filter(event.getClass()::isAssignableFrom).map(handlers::get).flatMap(List::stream).forEach(h -> h.handle(event)); - } - - /** - * @return a map of all {@link EventHandler} instances currently registered at - * this {@link EventBus} with the {@link Event} classes they are - * subscribed to as keys - * @since Envoy v0.2-alpha - */ - public Map>, List> getHandlers() { return handlers; } -} diff --git a/src/main/java/envoy/client/event/EventHandler.java b/src/main/java/envoy/client/event/EventHandler.java deleted file mode 100644 index ef3daea..0000000 --- a/src/main/java/envoy/client/event/EventHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -package envoy.client.event; - -/** - * Project: envoy-clientChess
- * File: EventHandler.javaEvent.java
- * Created: 04.12.2019
- * - * @author Kai S. K. Engelbart - */ -public interface EventHandler { - - /** - * Consumes an event dispatched by the event bus. - * - * @param event The event dispatched by the event bus, only of supported type - */ - void handle(Event event); -} diff --git a/src/main/java/envoy/client/event/MessageCreationEvent.java b/src/main/java/envoy/client/event/MessageCreationEvent.java index 28f1b58..d2e372d 100644 --- a/src/main/java/envoy/client/event/MessageCreationEvent.java +++ b/src/main/java/envoy/client/event/MessageCreationEvent.java @@ -1,6 +1,7 @@ package envoy.client.event; -import envoy.schema.Message; +import envoy.data.Message; +import envoy.event.MessageEvent; /** * Project: envoy-client
diff --git a/src/main/java/envoy/client/event/MessageEvent.java b/src/main/java/envoy/client/event/MessageEvent.java deleted file mode 100644 index 2e818e5..0000000 --- a/src/main/java/envoy/client/event/MessageEvent.java +++ /dev/null @@ -1,27 +0,0 @@ -package envoy.client.event; - -import envoy.schema.Message; - -/** - * Project: envoy-client
- * File: MessageCreationEvent.java
- * Created: 4 Dec 2019
- * - * @author Kai S. K. Engelbart - */ -public class MessageEvent implements Event { - - protected final Message message; - - /** - * Initializes a {@link MessageEvent} conveying information about a - * {@link Message} object. - * - * @param message the {@link Message} object to attach to this event - * @since Envoy v0.2-alpha - */ - public MessageEvent(Message message) { this.message = message; } - - @Override - public Message get() { return message; } -} diff --git a/src/main/java/envoy/client/event/MessageModificationEvent.java b/src/main/java/envoy/client/event/MessageModificationEvent.java index 0b83ef0..a5b7c41 100644 --- a/src/main/java/envoy/client/event/MessageModificationEvent.java +++ b/src/main/java/envoy/client/event/MessageModificationEvent.java @@ -1,6 +1,7 @@ package envoy.client.event; -import envoy.schema.Message; +import envoy.data.Message; +import envoy.event.MessageEvent; /** * Project: envoy-client
diff --git a/src/main/java/envoy/client/event/ThemeChangeEvent.java b/src/main/java/envoy/client/event/ThemeChangeEvent.java index ac07aa1..adb9707 100644 --- a/src/main/java/envoy/client/event/ThemeChangeEvent.java +++ b/src/main/java/envoy/client/event/ThemeChangeEvent.java @@ -1,6 +1,7 @@ package envoy.client.event; import envoy.client.ui.Theme; +import envoy.event.Event; /** * Project: envoy-client
diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index dc68e10..4f8b0dc 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -10,12 +10,13 @@ import javax.swing.*; import javax.swing.border.EmptyBorder; import envoy.client.*; -import envoy.client.event.EventBus; import envoy.client.event.ThemeChangeEvent; import envoy.client.ui.settings.SettingsScreen; import envoy.client.util.EnvoyLog; -import envoy.schema.Message; -import envoy.schema.User; +import envoy.data.Message; +import envoy.data.MessageBuilder; +import envoy.data.User; +import envoy.event.EventBus; /** * Project: envoy-client
@@ -164,7 +165,7 @@ public class ChatWindow extends JFrame { final JList selectedUserList = (JList) listSelectionEvent.getSource(); final User user = selectedUserList.getSelectedValue(); - currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getID() == user.getID()).findFirst().get(); + currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); // Set all unread messages in the chat to read readCurrentChat(); @@ -245,8 +246,15 @@ public class ChatWindow extends JFrame { if (!messageEnterTextArea.getText().isEmpty()) try { - // Create and send message object - final Message message = localDB.createMessage(messageEnterTextArea.getText(), currentChat.getRecipient().getID()); + // Create message + final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId()) + .setText(messageEnterTextArea.getText()) + .build(); + + // Send message + client.sendMessage(message); + + // Add message to LocalDB and update UI currentChat.appendMessage(message); messageList.setModel(currentChat.getModel()); @@ -275,7 +283,7 @@ public class ChatWindow extends JFrame { userListModel.addElement(user); // Check if user exists in local DB - if (localDB.getChats().stream().filter(c -> c.getRecipient().getID() == user.getID()).count() == 0) + if (localDB.getChats().stream().filter(c -> c.getRecipient().getId() == user.getId()).count() == 0) localDB.getChats().add(new Chat(user)); }); SwingUtilities.invokeLater(() -> userList.setModel(userListModel)); @@ -296,14 +304,15 @@ public class ChatWindow extends JFrame { // Synchronize try { - localDB.applySync(client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID()))); + // 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(); - localDB.clearUnreadMessagesSync(); + // TODO: Process unread messages + // localDB.addUnreadMessagesToLocalDB(); + // localDB.clearUnreadMessagesSync(); // Mark unread messages as read when they are in the current chat readCurrentChat(); @@ -317,14 +326,18 @@ public class ChatWindow extends JFrame { private void updateUserStates() { for (int i = 0; i < userList.getModel().getSize(); i++) for (int j = 0; j < localDB.getChats().size(); j++) - if (userList.getModel().getElementAt(i).getID() == localDB.getChats().get(j).getRecipient().getID()) + if (userList.getModel().getElementAt(i).getId() == localDB.getChats().get(j).getRecipient().getId()) userList.getModel().getElementAt(i).setStatus(localDB.getChats().get(j).getRecipient().getStatus()); } /** * Marks messages in the current chat as {@code READ}. */ - private void readCurrentChat() { if (currentChat != null) { localDB.setMessagesToRead(currentChat); } } + private void readCurrentChat() { + if (currentChat != null) { + // TODO: localDB.setMessagesToRead(currentChat); + } + } /** * Sets the {@link Client} used by this {@link ChatWindow}. If the client is diff --git a/src/main/java/envoy/client/ui/LoginDialog.java b/src/main/java/envoy/client/ui/LoginDialog.java new file mode 100644 index 0000000..db88176 --- /dev/null +++ b/src/main/java/envoy/client/ui/LoginDialog.java @@ -0,0 +1,118 @@ +package envoy.client.ui; + +import java.awt.*; +import java.security.NoSuchAlgorithmException; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; + +import envoy.data.LoginCredentials; + +/** + * Project: envoy-client
+ * File: LoginDialog.java
+ * Created: 01.01.2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class LoginDialog extends JDialog { + + private final JPanel contentPanel = new JPanel(); + + private static final long serialVersionUID = 352021600833907468L; + private JTextField textField; + private JPasswordField passwordField; + + private LoginCredentials credentials; + + /** + * Displays a dialog enabling the user to enter their user name and password. + * + * @since Envoy v0.3-alpha + */ + public LoginDialog() { + setSize(338, 123); + setLocationRelativeTo(null); + getContentPane().setLayout(new BorderLayout()); + contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(contentPanel, BorderLayout.CENTER); + GridBagLayout gbl_contentPanel = new GridBagLayout(); + gbl_contentPanel.columnWidths = new int[] { 0, 0, 0 }; + gbl_contentPanel.rowHeights = new int[] { 0, 0, 0 }; + gbl_contentPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; + gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE }; + contentPanel.setLayout(gbl_contentPanel); + { + JLabel lblUserName = new JLabel("User name:"); + GridBagConstraints gbc_lblUserName = new GridBagConstraints(); + gbc_lblUserName.anchor = GridBagConstraints.EAST; + gbc_lblUserName.insets = new Insets(0, 0, 5, 5); + gbc_lblUserName.gridx = 0; + gbc_lblUserName.gridy = 0; + contentPanel.add(lblUserName, gbc_lblUserName); + } + { + textField = new JTextField(); + GridBagConstraints gbc_textField = new GridBagConstraints(); + gbc_textField.insets = new Insets(0, 0, 5, 0); + gbc_textField.fill = GridBagConstraints.HORIZONTAL; + gbc_textField.gridx = 1; + gbc_textField.gridy = 0; + contentPanel.add(textField, gbc_textField); + textField.setColumns(10); + } + { + JLabel lblPassword = new JLabel("Password:"); + GridBagConstraints gbc_lblPassword = new GridBagConstraints(); + gbc_lblPassword.anchor = GridBagConstraints.EAST; + gbc_lblPassword.insets = new Insets(0, 0, 0, 5); + gbc_lblPassword.gridx = 0; + gbc_lblPassword.gridy = 1; + contentPanel.add(lblPassword, gbc_lblPassword); + } + { + passwordField = new JPasswordField(); + GridBagConstraints gbc_passwordField = new GridBagConstraints(); + gbc_passwordField.fill = GridBagConstraints.HORIZONTAL; + gbc_passwordField.gridx = 1; + gbc_passwordField.gridy = 1; + contentPanel.add(passwordField, gbc_passwordField); + } + { + JPanel buttonPane = new JPanel(); + buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + { + JButton okButton = new JButton("OK"); + okButton.addActionListener((evt) -> { + try { + credentials = new LoginCredentials(textField.getText(), passwordField.getPassword()); + dispose(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + }); + okButton.setActionCommand("OK"); + buttonPane.add(okButton); + getRootPane().setDefaultButton(okButton); + } + { + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener((evt) -> dispose()); + cancelButton.setActionCommand("Cancel"); + buttonPane.add(cancelButton); + } + } + + setModal(true); + setVisible(true); + } + + /** + * @return the {@link LoginCredentials} entered by the user, or {@code null} if + * the dialog has been cancelled + * @since Envoy v0.3-alpha + */ + public LoginCredentials getCredentials() { return credentials; } +} \ No newline at end of file diff --git a/src/main/java/envoy/client/ui/MessageListRenderer.java b/src/main/java/envoy/client/ui/MessageListRenderer.java index ddd2230..33f36f9 100644 --- a/src/main/java/envoy/client/ui/MessageListRenderer.java +++ b/src/main/java/envoy/client/ui/MessageListRenderer.java @@ -8,7 +8,7 @@ import javax.swing.JList; import javax.swing.ListCellRenderer; import envoy.client.Settings; -import envoy.schema.Message; +import envoy.data.Message; /** * Defines how a message is displayed.
@@ -37,19 +37,17 @@ public class MessageListRenderer extends JLabel implements ListCellRenderer

%s

%s :%s", dateColor, diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 8e57a08..3e4692d 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -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; @@ -12,8 +13,9 @@ import javax.swing.SwingUtilities; import envoy.client.*; import envoy.client.util.EnvoyLog; +import envoy.data.LoginCredentials; +import envoy.data.User; import envoy.exception.EnvoyException; -import envoy.schema.User; /** * Starts the Envoy client and prompts the user to enter their name.
@@ -69,11 +71,12 @@ public class Startup { EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier()); EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier()); - // 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); + // Ask the user for their user name and password + LoginCredentials credentials = new LoginDialog().getCredentials(); + + if (credentials == null) { + logger.info("The login process has been aborted by the user. Exiting..."); + System.exit(0); } // Initialize the local database @@ -91,20 +94,21 @@ public class Startup { // Acquire the client user (with ID) either from the server or from the local // database, which triggers offline mode - Client client = new Client(config); + Client client = new Client(); try { // Try entering online mode first - client.onlineInit(userName); + client.onlineInit(credentials); } catch (Exception e1) { logger.warning("Could not connect to server. Trying offline mode..."); + e1.printStackTrace(); try { // Try entering offline mode localDB.loadUsers(); - User clientUser = localDB.getUsers().get(userName); + User clientUser = localDB.getUsers().get(credentials.getName()); if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); client.setSender(clientUser); JOptionPane.showMessageDialog(null, - "A connection to the server could not be established. Starting in offline mode.", + "A connection to the server could not be established. Starting in offline mode.\n" + e1, "Connection error", JOptionPane.WARNING_MESSAGE); } catch (Exception e2) { @@ -121,16 +125,16 @@ public class Startup { try { localDB.initializeDBFile(); localDB.loadChats(); - } catch (EnvoyException e) { + } 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 + "\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()); @@ -142,8 +146,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!"); } @@ -155,13 +164,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); } })); } diff --git a/src/main/java/envoy/client/ui/StatusTrayIcon.java b/src/main/java/envoy/client/ui/StatusTrayIcon.java index 4670377..0cfda6a 100644 --- a/src/main/java/envoy/client/ui/StatusTrayIcon.java +++ b/src/main/java/envoy/client/ui/StatusTrayIcon.java @@ -1,21 +1,14 @@ package envoy.client.ui; -import java.awt.AWTException; -import java.awt.Image; -import java.awt.MenuItem; -import java.awt.PopupMenu; -import java.awt.SystemTray; -import java.awt.Toolkit; -import java.awt.TrayIcon; +import java.awt.*; import java.awt.TrayIcon.MessageType; -import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import envoy.client.event.EventBus; import envoy.client.event.MessageCreationEvent; +import envoy.data.Message; +import envoy.event.EventBus; import envoy.exception.EnvoyException; -import envoy.schema.Message; /** * Project: envoy-client
@@ -81,10 +74,13 @@ public class StatusTrayIcon { trayIcon.addActionListener((evt) -> { focusTarget.setVisible(true); focusTarget.requestFocus(); }); // Start processing message events - EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { - if (displayMessages) - trayIcon.displayMessage("New message received", ((MessageCreationEvent) evt).get().getContent().get(0).getText(), MessageType.INFO); - }); + EventBus.getInstance() + .register(MessageCreationEvent.class, + (evt) -> { + // TODO: Handle other message types + if (displayMessages) + trayIcon.displayMessage("New message received", ((MessageCreationEvent) evt).get().getText(), MessageType.INFO); + }); } /** @@ -101,4 +97,4 @@ public class StatusTrayIcon { throw new EnvoyException("Could not attach Envoy tray icon to system tray.", e); } } -} +} \ No newline at end of file diff --git a/src/main/java/envoy/client/ui/UserListRenderer.java b/src/main/java/envoy/client/ui/UserListRenderer.java index ca7214d..9441801 100644 --- a/src/main/java/envoy/client/ui/UserListRenderer.java +++ b/src/main/java/envoy/client/ui/UserListRenderer.java @@ -7,8 +7,8 @@ import javax.swing.JList; import javax.swing.ListCellRenderer; import envoy.client.Settings; -import envoy.schema.User; -import envoy.schema.User.UserStatus; +import envoy.data.User; +import envoy.data.User.UserStatus; /** * Defines how the {@code UserList} is displayed.
diff --git a/src/main/java/envoy/client/ui/settings/SettingsScreen.java b/src/main/java/envoy/client/ui/settings/SettingsScreen.java index 611100f..533200f 100644 --- a/src/main/java/envoy/client/ui/settings/SettingsScreen.java +++ b/src/main/java/envoy/client/ui/settings/SettingsScreen.java @@ -10,11 +10,11 @@ import java.util.logging.Logger; import javax.swing.*; import envoy.client.Settings; -import envoy.client.event.EventBus; import envoy.client.event.ThemeChangeEvent; import envoy.client.ui.PrimaryButton; import envoy.client.ui.Theme; import envoy.client.util.EnvoyLog; +import envoy.event.EventBus; /** * This class provides the GUI to change the user specific settings.
diff --git a/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java b/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java index ba5da87..9d4c6be 100644 --- a/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java +++ b/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java @@ -10,11 +10,11 @@ import java.util.logging.Logger; import javax.swing.*; import envoy.client.Settings; -import envoy.client.event.EventBus; import envoy.client.event.ThemeChangeEvent; import envoy.client.ui.Color; import envoy.client.ui.Theme; import envoy.client.util.EnvoyLog; +import envoy.event.EventBus; /** * Displays GUI components that allow changing the current {@Theme} and creating diff --git a/src/main/java/envoy/client/util/SerializationUtils.java b/src/main/java/envoy/client/util/SerializationUtils.java deleted file mode 100644 index f0cc70f..0000000 --- a/src/main/java/envoy/client/util/SerializationUtils.java +++ /dev/null @@ -1,57 +0,0 @@ -package envoy.client.util; - -import java.io.*; - -import envoy.exception.EnvoyException; - -/** - * Project: envoy-clientChess
- * File: SerializationUtils.javaEvent.java
- * Created: 23.12.2019
- * - * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha - */ -public class SerializationUtils { - - private SerializationUtils() {} - - /** - * Deserializes an arbitrary {@link Serializable} object from a file. - * - * @param the type of the object to deserialize - * @param file the file deserialize from - * @param serializedClass the class of the object to deserialize - * @return the deserialized object - * @throws EnvoyException if an error occurred during deserialization - * @since Envoy v0.3-alpha - */ - public static 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 new EnvoyException("Could not load serialized object", e); - } - } - - /** - * Serializes an arbitrary object to a file. - * - * @param file the file to serialize to - * @param obj the object to serialize - * @throws IOException if an error occurred during serialization - * @since Envoy v0.3-alpha - */ - public static void write(File file, Object 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(); - } - try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) { - out.writeObject(obj); - } - } -} \ No newline at end of file