diff --git a/.classpath b/.classpath index 2a83b68..b468bc4 100644 --- a/.classpath +++ b/.classpath @@ -6,11 +6,6 @@ - - - - - @@ -18,12 +13,6 @@ - - - - - - @@ -35,5 +24,16 @@ + + + + + + + + + + + diff --git a/.project b/.project index 45a395e..fdd0c87 100644 --- a/.project +++ b/.project @@ -5,11 +5,21 @@ + + org.eclipse.wst.common.project.facet.core.builder + + + org.eclipse.jdt.core.javabuilder + + org.eclipse.wst.validation.validationbuilder + + + org.eclipse.m2e.core.maven2Builder @@ -19,5 +29,6 @@ org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 2f5cc74..db24ee7 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,7 +1,14 @@ 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 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore org.eclipse.jdt.core.compiler.release=disabled diff --git a/.settings/org.eclipse.jpt.core.prefs b/.settings/org.eclipse.jpt.core.prefs new file mode 100644 index 0000000..8aa2bb2 --- /dev/null +++ b/.settings/org.eclipse.jpt.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +org.eclipse.jpt.core.platform=hibernate2_1 +org.eclipse.jpt.jpa.core.discoverAnnotatedClasses=true diff --git a/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 0000000..7961132 --- /dev/null +++ b/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..90f3be6 --- /dev/null +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.settings/org.hibernate.eclipse.console.prefs b/.settings/org.hibernate.eclipse.console.prefs new file mode 100644 index 0000000..a6ccf3c --- /dev/null +++ b/.settings/org.hibernate.eclipse.console.prefs @@ -0,0 +1,3 @@ +default.configuration=envoy-server-standalone +eclipse.preferences.version=1 +hibernate3.enabled=true diff --git a/pom.xml b/pom.xml index 5b81ded..c0ed70b 100644 --- a/pom.xml +++ b/pom.xml @@ -17,22 +17,34 @@ 1.8 + + + jitpack.io + https://jitpack.io + + + - informatik-ag-ngl + com.github.informatik-ag-ngl envoy-common - 0.2-alpha + develop-SNAPSHOT - informatik-ag-ngl + com.github.informatik-ag-ngl java-nio-server - 0.0.1-SNAPSHOT + master-SNAPSHOT org.hibernate hibernate-core 5.4.10.Final + + org.postgresql + postgresql + 42.2.9 + diff --git a/src/main/java/envoy/server/ConnectionManager.java b/src/main/java/envoy/server/ConnectionManager.java new file mode 100644 index 0000000..0ae8fcc --- /dev/null +++ b/src/main/java/envoy/server/ConnectionManager.java @@ -0,0 +1,79 @@ +package envoy.server; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +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 + */ +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<>(); + + private static ConnectionManager connectionManager = new ConnectionManager(); + + private ConnectionManager() {} + + /** + * @return a singleton instance of this object + * @since Envoy Server Standalone v0.1-alpha + */ + public static ConnectionManager getInstance() { return connectionManager; } + + @Override + public void socketCancelled(long socketId) { + if (!pendingSockets.remove(socketId)) + sockets.entrySet().stream().filter(e -> e.getValue() == socketId).forEach(e -> sockets.remove(e.getValue())); + } + + @Override + public void socketRegistered(long socketId) { pendingSockets.add(socketId); } + + /** + * Associates a socket ID with a user ID. + * + * @param userId the user ID + * @param socketId the socket ID + * @since Envoy Server Standalone v0.1-alpha + */ + public void registerUser(long userId, long socketId) { + sockets.put(userId, socketId); + pendingSockets.remove(userId); + } + + /** + * @param userId the ID of the user registered at the a socket + * @return the ID of the socket + * @since Envoy Server Standalone v0.1-alpha + */ + public long getSocketId(long userId) { return sockets.get(userId); } + + /** + * @param userId the ID of the user to check for + * @return {@code true} if the user is online + * @since Envoy Server Standalone v0.1-alpha + */ + public boolean isOnline(long userId) { return sockets.containsKey(userId); } +} diff --git a/src/main/java/envoy/server/LoginCredentialProcessor.java b/src/main/java/envoy/server/LoginCredentialProcessor.java deleted file mode 100644 index f208a3e..0000000 --- a/src/main/java/envoy/server/LoginCredentialProcessor.java +++ /dev/null @@ -1,29 +0,0 @@ -package envoy.server; - -import envoy.data.LoginCredentials; -import envoy.data.User; - -/** - * This {@link ObjectProcessor} handles {@link LoginCredentials}.
- *
- * Project: envoy-server-standalone
- * File: LoginCredentialProcessor.java
- * Created: 30.12.2019
- * - * @author Kai S. K. Engelbart - * @since Envoy Server Standalone v0.1-alpha - */ -public class LoginCredentialProcessor implements ObjectProcessor { - - // TODO: Acquire user IDs from database - private static long currentUserId = 1; - - @Override - public Class getInputClass() { return LoginCredentials.class; } - - @Override - public User process(LoginCredentials input) { - System.out.println("Received login credentials " + input); - return new User(currentUserId++, input.getName()); - } -} diff --git a/src/main/java/envoy/server/MessageProcessor.java b/src/main/java/envoy/server/MessageProcessor.java deleted file mode 100644 index ea52c55..0000000 --- a/src/main/java/envoy/server/MessageProcessor.java +++ /dev/null @@ -1,22 +0,0 @@ -package envoy.server; - -import envoy.data.Message; - -/** - * 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 input) { return null; } -} diff --git a/src/main/java/envoy/server/ObjectProcessor.java b/src/main/java/envoy/server/ObjectProcessor.java index 44c07e1..6828598 100644 --- a/src/main/java/envoy/server/ObjectProcessor.java +++ b/src/main/java/envoy/server/ObjectProcessor.java @@ -1,5 +1,9 @@ package envoy.server; +import java.io.IOException; + +import envoy.server.net.ObjectWriteProxy; + /** * This interface defines methods for processing objects of a specific * type incoming from a client.
@@ -10,21 +14,22 @@ package envoy.server; * * @author Kai S. K. Engelbart * @param type of the request object - * @param type of the response object * @since Envoy Server Standalone v0.1-alpha */ -public interface ObjectProcessor { +public interface ObjectProcessor { /** - * @return the Class of the request object + * @return the class of the request object * @since Envoy Server Standalone v0.1-alpha */ Class getInputClass(); /** - * @param input the request object - * @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 */ - U process(T input); + void process(T input, long socketId, ObjectWriteProxy writeProxy) throws IOException; } \ No newline at end of file diff --git a/src/main/java/envoy/server/Startup.java b/src/main/java/envoy/server/Startup.java index 58a8366..e2edb6a 100644 --- a/src/main/java/envoy/server/Startup.java +++ b/src/main/java/envoy/server/Startup.java @@ -6,6 +6,12 @@ import java.util.Set; 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.
*
@@ -26,10 +32,13 @@ public class Startup { * @since Envoy Server Standalone v0.1-alpha */ public static void main(String[] args) throws IOException { - Set> processors = new HashSet<>(); + 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(); + server.getSocketProcessor().registerSocketIdListener(ConnectionManager.getInstance()); } } \ No newline at end of file diff --git a/src/main/java/envoy/server/data/Message.java b/src/main/java/envoy/server/data/Message.java index 19e25a6..575da6a 100644 --- a/src/main/java/envoy/server/data/Message.java +++ b/src/main/java/envoy/server/data/Message.java @@ -4,6 +4,7 @@ import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; @@ -11,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 @@ -28,22 +30,34 @@ import envoy.data.MessageBuilder; @Entity @Table(name = "messages") @NamedQueries( - { @NamedQuery(query = "SELECT m FROM Message m WHERE m.recipient =:recipient AND m.state = 1", name = "getUnreadMessages"), @NamedQuery( - query = "SELECT m FROM Message m WHERE m.sender =:sender AND m.state = :state", - 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.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 = "getMessageById") } ) public class Message { @Id - private long id; - private User sender, recipient; + private long id; + + @ManyToOne + private User sender; + + @ManyToOne + 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; private byte[] attachment; @@ -62,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 e0de15f..5f4e28e 100644 --- a/src/main/java/envoy/server/data/User.java +++ b/src/main/java/envoy/server/data/User.java @@ -3,6 +3,7 @@ package envoy.server.data; import java.util.Date; import java.util.List; +import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -27,18 +28,47 @@ import javax.persistence.TemporalType; */ @Entity @Table(name = "users") -@NamedQuery(query = "SELECT u FROM DBUser u WHERE u.id = :id", name = "getUserById") +@NamedQuery(query = "SELECT u FROM User u WHERE u.id = :id", name = "getUserById") 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; - private List contacts; + + @ElementCollection + 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/ObjectMessageProcessor.java b/src/main/java/envoy/server/net/ObjectMessageProcessor.java similarity index 60% rename from src/main/java/envoy/server/ObjectMessageProcessor.java rename to src/main/java/envoy/server/net/ObjectMessageProcessor.java index 53e53da..226fb69 100644 --- a/src/main/java/envoy/server/ObjectMessageProcessor.java +++ b/src/main/java/envoy/server/net/ObjectMessageProcessor.java @@ -1,16 +1,16 @@ -package envoy.server; +package envoy.server.net; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.Set; import com.jenkov.nioserver.IMessageProcessor; import com.jenkov.nioserver.Message; import com.jenkov.nioserver.WriteProxy; +import envoy.server.ObjectProcessor; + /** * Handles incoming objects.
*
@@ -23,7 +23,7 @@ import com.jenkov.nioserver.WriteProxy; */ public class ObjectMessageProcessor implements IMessageProcessor { - private final Set> processors; + private final Set> processors; /** * The constructor to set the {@link ObjectProcessor}s. @@ -31,7 +31,7 @@ public class ObjectMessageProcessor implements IMessageProcessor { * @param processors the {@link ObjectProcessor} to set * @since Envoy Server Standalone v0.1-alpha */ - public ObjectMessageProcessor(Set> processors) { this.processors = processors; } + public ObjectMessageProcessor(Set> processors) { this.processors = processors; } @SuppressWarnings("unchecked") @Override @@ -42,22 +42,10 @@ public class ObjectMessageProcessor implements IMessageProcessor { // Process object processors.stream().filter(p -> p.getInputClass().isInstance(obj)).forEach((@SuppressWarnings("rawtypes") ObjectProcessor p) -> { - Object responseObj = p.process(p.getInputClass().cast(obj)); - if (responseObj != null) { - // Create message targeted at the client - Message response = writeProxy.getMessage(); - response.socketId = message.socketId; - - // Serialize object to byte array - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (ObjectOutputStream oout = new ObjectOutputStream(baos)) { - oout.writeObject(responseObj); - } catch (IOException e) { - e.printStackTrace(); - } - byte[] objBytes = baos.toByteArray(); - response.writeToMessage(objBytes); - writeProxy.enqueue(response); + try { + p.process(p.getInputClass().cast(obj), message.socketId, new ObjectWriteProxy(writeProxy)); + } catch (IOException e) { + e.printStackTrace(); } }); } catch (IOException | ClassNotFoundException e) { diff --git a/src/main/java/envoy/server/ObjectMessageReader.java b/src/main/java/envoy/server/net/ObjectMessageReader.java similarity index 75% rename from src/main/java/envoy/server/ObjectMessageReader.java rename to src/main/java/envoy/server/net/ObjectMessageReader.java index f0fa73a..51fa3df 100644 --- a/src/main/java/envoy/server/ObjectMessageReader.java +++ b/src/main/java/envoy/server/net/ObjectMessageReader.java @@ -1,14 +1,13 @@ -package envoy.server; +package envoy.server.net; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import com.jenkov.nioserver.IMessageReader; -import com.jenkov.nioserver.Message; -import com.jenkov.nioserver.MessageBuffer; -import com.jenkov.nioserver.Socket; +import com.jenkov.nioserver.*; + +import envoy.util.SerializationUtils; /** * This {@link IMessageReader} decodes serialized Java objects.
@@ -26,11 +25,6 @@ public class ObjectMessageReader implements IMessageReader { private Message nextMessage; private MessageBuffer messageBuffer; - private int fromByteArray(byte[] bytes, int offset) { - return ((bytes[offset] & 0xFF) << 24) | ((bytes[offset + 1] & 0xFF) << 16) | ((bytes[offset + 2] & 0xFF) << 8) - | ((bytes[offset + 3] & 0xFF) << 0); - } - @Override public List getMessages() { return completeMessages; } @@ -54,7 +48,7 @@ public class ObjectMessageReader implements IMessageReader { // Get message length if (nextMessage.length - nextMessage.offset < 4) return; - int length = fromByteArray(nextMessage.sharedArray, nextMessage.offset) + 4; + int length = SerializationUtils.bytesToInt(nextMessage.sharedArray, nextMessage.offset) + 4; if (nextMessage.length - nextMessage.offset >= length) { Message message = messageBuffer.getMessage(); diff --git a/src/main/java/envoy/server/net/ObjectWriteProxy.java b/src/main/java/envoy/server/net/ObjectWriteProxy.java new file mode 100644 index 0000000..2ec9223 --- /dev/null +++ b/src/main/java/envoy/server/net/ObjectWriteProxy.java @@ -0,0 +1,53 @@ +package envoy.server.net; + +import java.io.IOException; + +import com.jenkov.nioserver.Message; +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
+ * + * @author Kai S. K. Engelbart + * @since Envoy Server Standalone v0.1-alpha + */ +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(); + response.socketId = recipientSocketId; + + // Serialize object to byte array + byte[] objBytes = SerializationUtils.writeToByteArray(obj); + + // Acquire object length in bytes + byte[] objLen = SerializationUtils.intToBytes(objBytes.length); + + response.writeToMessage(objLen); + response.writeToMessage(objBytes); + writeProxy.enqueue(response); + } +} 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/processors/LoginCredentialProcessor.java b/src/main/java/envoy/server/processors/LoginCredentialProcessor.java new file mode 100644 index 0000000..e94ce91 --- /dev/null +++ b/src/main/java/envoy/server/processors/LoginCredentialProcessor.java @@ -0,0 +1,61 @@ +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; + +/** + * This {@link ObjectProcessor} handles {@link LoginCredentials}.
+ *
+ * Project: envoy-server-standalone
+ * File: LoginCredentialProcessor.java
+ * Created: 30.12.2019
+ * + * @author Kai S. K. Engelbart + * @since Envoy Server Standalone v0.1-alpha + */ +public class LoginCredentialProcessor implements ObjectProcessor { + + // TODO: Acquire user IDs from database + private static long currentUserId = 1; + + @Override + public Class getInputClass() { return LoginCredentials.class; } + + @Override + public void process(LoginCredentials input, long socketId, ObjectWriteProxy writeProxy) throws IOException { + System.out.println(String.format("Received login credentials %s from socket ID %d", input, socketId)); + + // Create user + User user = new User(currentUserId++, input.getName()); + ConnectionManager.getInstance().registerUser(socketId, user.getId()); + + // Create contacts + Contacts contacts = new Contacts(user.getId(), new ArrayList<>()); + + // Complete handshake + System.out.println("Sending user..."); + writeProxy.write(socketId, user); + System.out.println("Sending contacts..."); + writeProxy.write(socketId, contacts); + System.out.println("Sending unread messages and updating them in the database..."); + List 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)); + } +} diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..4d937c9 --- /dev/null +++ b/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file