Added writing capabilities to ObjectProcessor, completed db integration

At this moment the client is not able to receive to objects sent
consecutively. This will be worked on in a future commit and should be
fixed before merging this branch into develop.
This commit is contained in:
Kai S. K. Engelbart 2020-01-06 14:58:28 +02:00
parent 597385c950
commit 26fc4374ca
9 changed files with 128 additions and 36 deletions

View File

@ -33,6 +33,11 @@
<artifactId>hibernate-core</artifactId> <artifactId>hibernate-core</artifactId>
<version>5.4.10.Final</version> <version>5.4.10.Final</version>
</dependency> </dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.9</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,7 +1,12 @@
package envoy.server; package envoy.server;
import java.io.IOException;
import java.util.ArrayList;
import envoy.data.Contacts;
import envoy.data.LoginCredentials; import envoy.data.LoginCredentials;
import envoy.data.User; import envoy.data.User;
import envoy.server.net.ObjectWriteProxy;
/** /**
* This {@link ObjectProcessor} handles {@link LoginCredentials}.<br> * This {@link ObjectProcessor} handles {@link LoginCredentials}.<br>
@ -13,7 +18,7 @@ import envoy.data.User;
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public class LoginCredentialProcessor implements ObjectProcessor<LoginCredentials, User> { public class LoginCredentialProcessor implements ObjectProcessor<LoginCredentials> {
// TODO: Acquire user IDs from database // TODO: Acquire user IDs from database
private static long currentUserId = 1; private static long currentUserId = 1;
@ -22,10 +27,20 @@ public class LoginCredentialProcessor implements ObjectProcessor<LoginCredential
public Class<LoginCredentials> getInputClass() { return LoginCredentials.class; } public Class<LoginCredentials> getInputClass() { return LoginCredentials.class; }
@Override @Override
public User process(LoginCredentials input, long socketId) { 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)); System.out.println(String.format("Received login credentials %s from socket ID %d", input, socketId));
// Create user
User user = new User(currentUserId++, input.getName()); User user = new User(currentUserId++, input.getName());
ConnectionManager.getInstance().registerUser(socketId, user.getId()); ConnectionManager.getInstance().registerUser(socketId, user.getId());
return user;
// 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);
} }
} }

View File

@ -1,6 +1,7 @@
package envoy.server; package envoy.server;
import envoy.data.Message; import envoy.data.Message;
import envoy.server.net.ObjectWriteProxy;
/** /**
* This {@link ObjectProcessor} handles incoming {@link Message}s.<br> * This {@link ObjectProcessor} handles incoming {@link Message}s.<br>
@ -12,13 +13,13 @@ import envoy.data.Message;
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public class MessageProcessor implements ObjectProcessor<Message, Void> { public class MessageProcessor implements ObjectProcessor<Message> {
@Override @Override
public Class<Message> getInputClass() { return Message.class; } public Class<Message> getInputClass() { return Message.class; }
@Override @Override
public Void process(Message message, long socketId) { public void process(Message message, long socketId, ObjectWriteProxy writeProxy) {
// TODO: Send message to recipient if online // TODO: Send message to recipient if online
ConnectionManager connectionManager = ConnectionManager.getInstance(); ConnectionManager connectionManager = ConnectionManager.getInstance();
@ -27,6 +28,5 @@ public class MessageProcessor implements ObjectProcessor<Message, Void> {
} }
// TODO: Add message to database // TODO: Add message to database
return null;
} }
} }

View File

@ -1,5 +1,9 @@
package envoy.server; package envoy.server;
import java.io.IOException;
import envoy.server.net.ObjectWriteProxy;
/** /**
* This interface defines methods for processing objects of a specific * This interface defines methods for processing objects of a specific
* type incoming from a client.<br> * type incoming from a client.<br>
@ -10,13 +14,12 @@ package envoy.server;
* *
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @param <T> type of the request object * @param <T> type of the request object
* @param <U> type of the response object
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public interface ObjectProcessor<T, U> { public interface ObjectProcessor<T> {
/** /**
* @return the Class of the request object * @return the class of the request object
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
Class<T> getInputClass(); Class<T> getInputClass();
@ -27,5 +30,5 @@ public interface ObjectProcessor<T, U> {
* @return the response object * @return the response object
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
U process(T input, long socketId); void process(T input, long socketId, ObjectWriteProxy writeProxy) throws IOException;
} }

View File

@ -4,6 +4,7 @@ import java.util.Date;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery; import javax.persistence.NamedQuery;
import javax.persistence.Table; import javax.persistence.Table;
@ -28,8 +29,12 @@ import envoy.data.MessageBuilder;
@Entity @Entity
@Table(name = "messages") @Table(name = "messages")
@NamedQueries( @NamedQueries(
{ @NamedQuery(query = "SELECT m FROM Message m WHERE m.recipient =:recipient AND m.state = 1", name = "getUnreadMessages"), @NamedQuery( { @NamedQuery(
query = "SELECT m FROM Message m WHERE m.sender =:sender AND m.state = :state", 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? 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.id = :messageId", name = "get message") }//TODO do we need this namedQuery?
) )
@ -37,13 +42,22 @@ public class Message {
@Id @Id
private long id; private long id;
private User sender, recipient;
@ManyToOne
private User sender;
@ManyToOne
private User recipient;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date creationDate; private Date creationDate;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date receivedDate; private Date receivedDate;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date readDate; private Date readDate;
private envoy.data.Message.MessageStatus status; private envoy.data.Message.MessageStatus status;
private String text; private String text;
private byte[] attachment; private byte[] attachment;

View File

@ -3,6 +3,7 @@ package envoy.server.data;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType; import javax.persistence.GenerationType;
@ -27,7 +28,7 @@ import javax.persistence.TemporalType;
*/ */
@Entity @Entity
@Table(name = "users") @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 { public class User {
@Id @Id
@ -35,9 +36,12 @@ public class User {
private long id; private long id;
private String name; private String name;
private byte[] passwordHash; private byte[] passwordHash;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date lastSeen; private Date lastSeen;
private envoy.data.User.UserStatus status; private envoy.data.User.UserStatus status;
@ElementCollection
private List<User> contacts; private List<User> contacts;
/** /**

View File

@ -1,10 +1,8 @@
package envoy.server.net; package envoy.server.net;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Set; import java.util.Set;
import com.jenkov.nioserver.IMessageProcessor; import com.jenkov.nioserver.IMessageProcessor;
@ -25,7 +23,7 @@ import envoy.server.ObjectProcessor;
*/ */
public class ObjectMessageProcessor implements IMessageProcessor { public class ObjectMessageProcessor implements IMessageProcessor {
private final Set<ObjectProcessor<?, ?>> processors; private final Set<ObjectProcessor<?>> processors;
/** /**
* The constructor to set the {@link ObjectProcessor}s. * The constructor to set the {@link ObjectProcessor}s.
@ -33,9 +31,7 @@ public class ObjectMessageProcessor implements IMessageProcessor {
* @param processors the {@link ObjectProcessor} to set * @param processors the {@link ObjectProcessor} to set
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public ObjectMessageProcessor(Set<ObjectProcessor<?, ?>> processors) { public ObjectMessageProcessor(Set<ObjectProcessor<?>> processors) { this.processors = processors; }
this.processors = processors;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -45,25 +41,17 @@ public class ObjectMessageProcessor implements IMessageProcessor {
System.out.println("Read object: " + obj.toString()); System.out.println("Read object: " + obj.toString());
// Process object // Process object
processors.stream().filter(p -> p.getInputClass().isInstance(obj)).forEach((@SuppressWarnings("rawtypes") ObjectProcessor p) -> { processors.stream()
Object responseObj = p.process(p.getInputClass().cast(obj), message.socketId); .filter(p -> p.getInputClass().isInstance(obj))
if (responseObj != null) { .forEach((@SuppressWarnings(
// Create message targeted at the client "rawtypes"
Message response = writeProxy.getMessage(); ) ObjectProcessor p) -> {
response.socketId = message.socketId; try {
p.process(p.getInputClass().cast(obj), message.socketId, new ObjectWriteProxy(writeProxy));
// Serialize object to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oout = new ObjectOutputStream(baos)) {
oout.writeObject(responseObj);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
byte[] objBytes = baos.toByteArray(); });
response.writeToMessage(objBytes);
writeProxy.enqueue(response);
}
});
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -0,0 +1,34 @@
package envoy.server.net;
import java.io.IOException;
import com.jenkov.nioserver.Message;
import com.jenkov.nioserver.WriteProxy;
import envoy.util.SerializationUtils;
/**
* Project: <strong>envoy-server-standalone</strong><br>
* File: <strong>ObjectWriteProxy.java</strong><br>
* Created: <strong>04.01.2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy Server Standalone v0.1-alpha
*/
public class ObjectWriteProxy {
private final WriteProxy writeProxy;
public ObjectWriteProxy(WriteProxy writeProxy) { this.writeProxy = writeProxy; }
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);
response.writeToMessage(objBytes);
writeProxy.enqueue(response);
}
}

View File

@ -0,0 +1,29 @@
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="envoy"
transaction-type="RESOURCE_LOCAL">
<properties>
<property name="javax.persistence.jdbc.driver"
value="org.postgresql.Driver" /> <!-- DB Driver -->
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://localhost/envoy" /> <!-- BD Mane -->
<property name="javax.persistence.jdbc.user" value="envoy" /> <!-- DB User -->
<property name="javax.persistence.jdbc.password"
value="envoy" /> <!-- DB Password -->
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQL95Dialect" /> <!-- DB Dialect -->
<property name="hibernate.hbm2ddl.auto" value="update" /> <!-- create / create-drop / update -->
<property name="hibernate.show_sql" value="true" /> <!-- Show SQL in console -->
<property name="hibernate.format_sql" value="true" /> <!-- Show SQL formatted -->
</properties>
</persistence-unit>
</persistence>