From fb4ecaed4e92992d8c9c2440bf2af4b740131caf Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 11 Jan 2020 18:28:48 +0100 Subject: [PATCH] Added EventProcessor and methods to handle MessageStatus changes additionally cleaned up whole project, fixed some Javadoc errors and added a few database and connection options. Sorry for the huge commit, there was almost no time inbetween where a commit would have been possible, as to solve every problem, a new problem arose. However, as of now, f/message_handling should be ready to be merged into develop, besides that it could not be tested yet. --- .settings/org.eclipse.jdt.core.prefs | 4 + .../java/envoy/server/ConnectionManager.java | 16 ++-- .../java/envoy/server/MessageProcessor.java | 32 -------- .../java/envoy/server/ObjectProcessor.java | 7 +- src/main/java/envoy/server/Startup.java | 4 + src/main/java/envoy/server/data/Message.java | 29 ++++--- src/main/java/envoy/server/data/User.java | 34 +++++++- .../server/database/PersistenceManager.java | 28 ++++++- .../server/net/ObjectMessageProcessor.java | 18 ++--- .../envoy/server/net/ObjectWriteProxy.java | 14 ++++ .../server/processors/EventProcessor.java | 77 +++++++++++++++++++ .../LoginCredentialProcessor.java | 17 +++- .../server/processors/MessageProcessor.java | 47 +++++++++++ 13 files changed, 255 insertions(+), 72 deletions(-) delete mode 100644 src/main/java/envoy/server/MessageProcessor.java create mode 100644 src/main/java/envoy/server/processors/EventProcessor.java rename src/main/java/envoy/server/{ => processors}/LoginCredentialProcessor.java (66%) create mode 100644 src/main/java/envoy/server/processors/MessageProcessor.java diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index cac0df4..db24ee7 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,7 +1,11 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error diff --git a/src/main/java/envoy/server/ConnectionManager.java b/src/main/java/envoy/server/ConnectionManager.java index ac4b0c0..0ae8fcc 100644 --- a/src/main/java/envoy/server/ConnectionManager.java +++ b/src/main/java/envoy/server/ConnectionManager.java @@ -11,7 +11,7 @@ import com.jenkov.nioserver.ISocketIdListener; * Project: envoy-server-standalone
* File: ConnectionManager.java
* Created: 03.01.2020
- * + * * @author Kai S. K. Engelbart * @since Envoy Server Standalone v0.1-alpha */ @@ -20,14 +20,14 @@ public class ConnectionManager implements ISocketIdListener { /** * Contains all socket IDs that have not yet performed a handshake / acquired * their corresponding user ID. - * + * * @since Envoy Server Standalone v0.1-alpha */ private Set pendingSockets = new HashSet<>(); /** * Contains all socket IDs that have acquired a user ID as keys to these IDs. - * + * * @since Envoy Server Standalone v0.1-alpha */ private Map sockets = new HashMap<>(); @@ -53,14 +53,14 @@ public class ConnectionManager implements ISocketIdListener { /** * Associates a socket ID with a user ID. - * - * @param socketId the socket ID + * * @param userId the user ID + * @param socketId the socket ID * @since Envoy Server Standalone v0.1-alpha */ - public void registerUser(long socketId, long userId) { - sockets.put(socketId, userId); - pendingSockets.remove(socketId); + public void registerUser(long userId, long socketId) { + sockets.put(userId, socketId); + pendingSockets.remove(userId); } /** diff --git a/src/main/java/envoy/server/MessageProcessor.java b/src/main/java/envoy/server/MessageProcessor.java deleted file mode 100644 index 3e2f139..0000000 --- a/src/main/java/envoy/server/MessageProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -package envoy.server; - -import envoy.data.Message; -import envoy.server.net.ObjectWriteProxy; - -/** - * This {@link ObjectProcessor} handles incoming {@link Message}s.
- *
- * Project: envoy-server-standalone
- * File: MessageProcessor.java
- * Created: 30.12.2019
- * - * @author Kai S. K. Engelbart - * @since Envoy Server Standalone v0.1-alpha - */ -public class MessageProcessor implements ObjectProcessor { - - @Override - public Class getInputClass() { return Message.class; } - - @Override - public void process(Message message, long socketId, ObjectWriteProxy writeProxy) { - - // TODO: Send message to recipient if online - ConnectionManager connectionManager = ConnectionManager.getInstance(); - if (connectionManager.isOnline(message.getRecipientId())) { - - } - - // TODO: Add message to database - } -} diff --git a/src/main/java/envoy/server/ObjectProcessor.java b/src/main/java/envoy/server/ObjectProcessor.java index 0221fc0..6828598 100644 --- a/src/main/java/envoy/server/ObjectProcessor.java +++ b/src/main/java/envoy/server/ObjectProcessor.java @@ -25,9 +25,10 @@ public interface ObjectProcessor { Class getInputClass(); /** - * @param input the request object - * @param socketId the ID of the socket from which the object was received - * @return the response object + * @param input the request object + * @param socketId the ID of the socket from which the object was received + * @param writeProxy the object that allows writing to a client + * @throws IOException if something went wrong during processing * @since Envoy Server Standalone v0.1-alpha */ void process(T input, long socketId, ObjectWriteProxy writeProxy) throws IOException; diff --git a/src/main/java/envoy/server/Startup.java b/src/main/java/envoy/server/Startup.java index 58931ff..e2edb6a 100644 --- a/src/main/java/envoy/server/Startup.java +++ b/src/main/java/envoy/server/Startup.java @@ -8,6 +8,9 @@ import com.jenkov.nioserver.Server; import envoy.server.net.ObjectMessageProcessor; import envoy.server.net.ObjectMessageReader; +import envoy.server.processors.EventProcessor; +import envoy.server.processors.LoginCredentialProcessor; +import envoy.server.processors.MessageProcessor; /** * Starts the server.
@@ -32,6 +35,7 @@ public class Startup { Set> processors = new HashSet<>(); processors.add(new LoginCredentialProcessor()); processors.add(new MessageProcessor()); + processors.add(new EventProcessor()); // new PersistenceManager(); Server server = new Server(8080, () -> new ObjectMessageReader(), new ObjectMessageProcessor(processors)); server.start(); diff --git a/src/main/java/envoy/server/data/Message.java b/src/main/java/envoy/server/data/Message.java index af7f612..575da6a 100644 --- a/src/main/java/envoy/server/data/Message.java +++ b/src/main/java/envoy/server/data/Message.java @@ -12,6 +12,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import envoy.data.MessageBuilder; +import envoy.server.database.PersistenceManager; /** * This class serves as a way to let Hibernate communicate with the server @@ -32,16 +33,15 @@ import envoy.data.MessageBuilder; { @NamedQuery( query = "SELECT m FROM Message m WHERE m.recipient =:recipient AND m.status = envoy.data.Message$MessageStatus.SENT", name = "getUnreadMessages" - ), - @NamedQuery( - query = "SELECT m FROM Message m WHERE m.sender =:sender AND m.status = :status", - name = "find read messages"//TODO do we need this namedQuery? - ), @NamedQuery(query = "SELECT m FROM Message m WHERE m.id = :messageId", name = "get message") }//TODO do we need this namedQuery? + ), @NamedQuery( + query = "SELECT m FROM Message m WHERE m.sender =:sender AND m.status = :status", + name = "find read messages"// TODO do we need this namedQuery? + ), @NamedQuery(query = "SELECT m FROM Message m WHERE m.id = :messageId", name = "getMessageById") } ) public class Message { @Id - private long id; + private long id; @ManyToOne private User sender; @@ -50,13 +50,13 @@ public class Message { private User recipient; @Temporal(TemporalType.TIMESTAMP) - private Date creationDate; + private Date creationDate; @Temporal(TemporalType.TIMESTAMP) - private Date receivedDate; + private Date receivedDate; @Temporal(TemporalType.TIMESTAMP) - private Date readDate; + private Date readDate; private envoy.data.Message.MessageStatus status; private String text; @@ -76,9 +76,14 @@ public class Message { * @since Envoy Server Standalone v0.1-alpha */ public Message(envoy.data.Message message) { - id = message.getId(); - status = message.getStatus(); - text = message.getText(); + PersistenceManager persMan = PersistenceManager.getPersistenceManager(); + id = message.getId(); + status = message.getStatus(); + text = message.getText(); + creationDate = message.getCreationDate(); + sender = persMan.getUserById(message.getSenderId()); + recipient = persMan.getUserById(message.getRecipientId()); + // attachment = message.getAttachment().toByteArray();DOES NOT WORK YET } /** diff --git a/src/main/java/envoy/server/data/User.java b/src/main/java/envoy/server/data/User.java index 40e25c7..5f4e28e 100644 --- a/src/main/java/envoy/server/data/User.java +++ b/src/main/java/envoy/server/data/User.java @@ -33,16 +33,42 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private long id; - private String name; - private byte[] passwordHash; + private long id; + private String name; + private byte[] passwordHash; @Temporal(TemporalType.TIMESTAMP) private Date lastSeen; private envoy.data.User.UserStatus status; @ElementCollection - private List contacts; + private List contacts; + + /** + * Creates an instance of @link{User}. + * Solely used for JPA/ Hibernate + * + * @since Envoy Server Standalone v0.1-alpha + */ + public User() {} + + /** + * Creates an instance of @link{User}. + * + * @param user the {@link envoy.data.User} to convert + * @since Envoy Server Standalone v0.1-alpha + */ + public User(envoy.data.User user) { + id = user.getId(); + name = user.getName(); + status = user.getStatus(); + } + + /** + * @return a database {@link User} converted into an {@link envoy.data.User} + * @since Envoy Server Standalone v0.1-alpha + */ + public envoy.data.User toCommonUser() { return new envoy.data.User(this.id, this.name); } /** * @return the id of a {link envoy.data.User} diff --git a/src/main/java/envoy/server/database/PersistenceManager.java b/src/main/java/envoy/server/database/PersistenceManager.java index e7c2dc8..fb38a9e 100644 --- a/src/main/java/envoy/server/database/PersistenceManager.java +++ b/src/main/java/envoy/server/database/PersistenceManager.java @@ -20,6 +20,21 @@ import envoy.server.data.User; */ public class PersistenceManager { + private static final PersistenceManager persistenceManager = new PersistenceManager(); + + /** + * Creates the singleton instance of the @link{PersistenceManager}. + * + * @since Envoy Server Standalone v0.1-alpha + */ + private PersistenceManager() {} + + /** + * @return the {@link PersistenceManager} singleton + * @since Envoy Server Standalone v0.1-alpha + */ + public static PersistenceManager getPersistenceManager() { return persistenceManager; } + private EntityManager entityManager = Persistence.createEntityManagerFactory("envoy").createEntityManager(); /** @@ -55,7 +70,7 @@ public class PersistenceManager { public void updateMessage(Message message) { entityManager.unwrap(Session.class).merge(message); } /** - * Searches for a user with a specific id. + * Searches for a {@link User} with a specific id. * * @param id - the id to search for * @return the user with the specified id @@ -63,6 +78,17 @@ public class PersistenceManager { */ public User getUserById(long id) { return (User) entityManager.createNamedQuery("getUserById").setParameter("id", id).getSingleResult(); } + /** + * Searches for a {@link Message} with a specific id. + * + * @param id - the id to search for + * @return the message with the specified id + * @since Envoy Server Standalone v0.1-alpha + */ + public Message getMessageById(long id) { + return (Message) entityManager.createNamedQuery("getMessageById").setParameter("id", id).getSingleResult(); + } + /** * Returns all messages received while being offline. * diff --git a/src/main/java/envoy/server/net/ObjectMessageProcessor.java b/src/main/java/envoy/server/net/ObjectMessageProcessor.java index 47dae57..226fb69 100644 --- a/src/main/java/envoy/server/net/ObjectMessageProcessor.java +++ b/src/main/java/envoy/server/net/ObjectMessageProcessor.java @@ -41,17 +41,13 @@ public class ObjectMessageProcessor implements IMessageProcessor { System.out.println("Read object: " + obj.toString()); // Process object - processors.stream() - .filter(p -> p.getInputClass().isInstance(obj)) - .forEach((@SuppressWarnings( - "rawtypes" - ) ObjectProcessor p) -> { - try { - p.process(p.getInputClass().cast(obj), message.socketId, new ObjectWriteProxy(writeProxy)); - } catch (IOException e) { - e.printStackTrace(); - } - }); + processors.stream().filter(p -> p.getInputClass().isInstance(obj)).forEach((@SuppressWarnings("rawtypes") ObjectProcessor p) -> { + try { + p.process(p.getInputClass().cast(obj), message.socketId, new ObjectWriteProxy(writeProxy)); + } catch (IOException e) { + e.printStackTrace(); + } + }); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } diff --git a/src/main/java/envoy/server/net/ObjectWriteProxy.java b/src/main/java/envoy/server/net/ObjectWriteProxy.java index bd6baf4..2ec9223 100644 --- a/src/main/java/envoy/server/net/ObjectWriteProxy.java +++ b/src/main/java/envoy/server/net/ObjectWriteProxy.java @@ -8,6 +8,8 @@ import com.jenkov.nioserver.WriteProxy; import envoy.util.SerializationUtils; /** + * This class defines methods to send an object to a client.
+ *
* Project: envoy-server-standalone
* File: ObjectWriteProxy.java
* Created: 04.01.2020
@@ -19,8 +21,20 @@ public class ObjectWriteProxy { private final WriteProxy writeProxy; + /** + * Creates an instance of @link{ObjectWriteProxy}. + * + * @param writeProxy the {@link WriteProxy} to write objects to another client + * @since Envoy Server Standalone v0.1-alpha + */ public ObjectWriteProxy(WriteProxy writeProxy) { this.writeProxy = writeProxy; } + /** + * @param recipientSocketId the socket id of the recipient + * @param obj the object to return to the client + * @throws IOException if the serialization of the object failed + * @since Envoy Server Standalone v0.1-alpha + */ public void write(long recipientSocketId, Object obj) throws IOException { // Create message targeted at the client Message response = writeProxy.getMessage(); diff --git a/src/main/java/envoy/server/processors/EventProcessor.java b/src/main/java/envoy/server/processors/EventProcessor.java new file mode 100644 index 0000000..58c769f --- /dev/null +++ b/src/main/java/envoy/server/processors/EventProcessor.java @@ -0,0 +1,77 @@ +package envoy.server.processors; + +import java.io.IOException; + +import envoy.data.Message; +import envoy.data.Message.MessageStatus; +import envoy.event.Event; +import envoy.event.MessageStatusChangeEvent; +import envoy.exception.EnvoyException; +import envoy.server.ConnectionManager; +import envoy.server.ObjectProcessor; +import envoy.server.database.PersistenceManager; +import envoy.server.net.ObjectWriteProxy; + +/** + * Project: envoy-server-standalone
+ * File: EventProcessor.java
+ * Created: 10 Jan 2020
+ * + * @author Leon Hofmeister + * @since Envoy Server Standalone v0.1-alpha + */ +public class EventProcessor implements ObjectProcessor> { + + private Event event; + + /** + * Creates an instance of @link{EventProcessor}. + * + * @since Envoy Server Standalone v0.1-alpha + */ + public EventProcessor() {} + + @SuppressWarnings("unchecked") + @Override + public Class> getInputClass() { return (Class>) event.getClass(); } + + @Override + public void process(Event input, long socketId, ObjectWriteProxy writeProxy) throws IOException { + event = input; + if (event instanceof MessageStatusChangeEvent) try { + applyMessageStatusChange((MessageStatusChangeEvent) event, writeProxy); + } catch (EnvoyException e) { + e.printStackTrace(); + } + } + + /** + * Redirects messageStatus changes to the database and to the recipient of the + * {@link Message}. + * + * @param event the {@link MessageStatusChangeEvent} to adjust + * @throws EnvoyException if the {@link Message} has an invalid state + * @since Envoy Server Standalone v0.1-alpha + */ + private void applyMessageStatusChange(MessageStatusChangeEvent event, ObjectWriteProxy writeProxy) throws EnvoyException { + if (!(event.get() == MessageStatus.READ))// check that no invalid MessageStatuses are sent + throw new EnvoyException("Message" + event.getId() + "has an invalid status"); + + ConnectionManager conMan = ConnectionManager.getInstance(); + PersistenceManager perMan = PersistenceManager.getPersistenceManager(); + envoy.server.data.Message msg = perMan.getMessageById(event.getId()); + + msg.setStatus(event.get()); + msg.setReadDate(event.getDate()); + + if (conMan.isOnline(msg.getRecipient().getId())) try { + writeProxy.write(conMan.getSocketId(msg.getRecipient().getId()), event); + } catch (IOException e) { + System.err.println("Recipient online. Failed to send MessageStatusChangedEvent at message" + event.getId()); + e.printStackTrace(); + } + perMan.updateMessage(msg); + + } + +} diff --git a/src/main/java/envoy/server/LoginCredentialProcessor.java b/src/main/java/envoy/server/processors/LoginCredentialProcessor.java similarity index 66% rename from src/main/java/envoy/server/LoginCredentialProcessor.java rename to src/main/java/envoy/server/processors/LoginCredentialProcessor.java index 89261cf..e94ce91 100644 --- a/src/main/java/envoy/server/LoginCredentialProcessor.java +++ b/src/main/java/envoy/server/processors/LoginCredentialProcessor.java @@ -1,11 +1,18 @@ -package envoy.server; +package envoy.server.processors; import java.io.IOException; import java.util.ArrayList; +import java.util.Date; +import java.util.List; import envoy.data.Contacts; import envoy.data.LoginCredentials; +import envoy.data.Message.MessageStatus; import envoy.data.User; +import envoy.server.ConnectionManager; +import envoy.server.ObjectProcessor; +import envoy.server.data.Message; +import envoy.server.database.PersistenceManager; import envoy.server.net.ObjectWriteProxy; /** @@ -42,5 +49,13 @@ public class LoginCredentialProcessor implements ObjectProcessor pendingMessages = PersistenceManager.getPersistenceManager().getUnreadMessages(new envoy.server.data.User(user)); + pendingMessages.forEach((msg) -> { + msg.setReceivedDate(new Date()); + msg.setStatus(MessageStatus.RECEIVED); + PersistenceManager.getPersistenceManager().updateMessage(msg); + }); + writeProxy.write(socketId, pendingMessages); } } diff --git a/src/main/java/envoy/server/processors/MessageProcessor.java b/src/main/java/envoy/server/processors/MessageProcessor.java new file mode 100644 index 0000000..ce857de --- /dev/null +++ b/src/main/java/envoy/server/processors/MessageProcessor.java @@ -0,0 +1,47 @@ +package envoy.server.processors; + +import java.io.IOException; +import java.util.Date; + +import envoy.data.Message; +import envoy.event.MessageStatusChangeEvent; +import envoy.server.ConnectionManager; +import envoy.server.ObjectProcessor; +import envoy.server.database.PersistenceManager; +import envoy.server.net.ObjectWriteProxy; + +/** + * This {@link ObjectProcessor} handles incoming {@link Message}s.
+ *
+ * Project: envoy-server-standalone
+ * File: MessageProcessor.java
+ * Created: 30.12.2019
+ * + * @author Kai S. K. Engelbart + * @since Envoy Server Standalone v0.1-alpha + */ +public class MessageProcessor implements ObjectProcessor { + + @Override + public Class getInputClass() { return Message.class; } + + @Override + public void process(Message message, long socketId, ObjectWriteProxy writeProxy) { + + ConnectionManager connectionManager = ConnectionManager.getInstance(); + message.nextStatus(); + if (connectionManager.isOnline(message.getRecipientId())) try {// if recipient is online, he receives the message directly + writeProxy.write(connectionManager.getSocketId(message.getRecipientId()), message); + } catch (IOException e) { + System.err.println("Recipient online. Failed to send message" + message.getId()); + e.printStackTrace(); + } + try {// sender receives confirmation that the server received the message + writeProxy.write(connectionManager.getSocketId(message.getSenderId()), + new MessageStatusChangeEvent(message.getId(), message.getStatus(), new Date())); + } catch (IOException e) { + e.printStackTrace(); + } + PersistenceManager.getPersistenceManager().addMessage(new envoy.server.data.Message(message)); + } +}