Creating a WhatsApp Clone - Part XII
Lets continue with the other entities. I’ll skip media as it’s just a copy and paste of the facebook media class and partially implemented to boot.
@Entity
@Indexed
public class ChatGroup {
@Id
private String id;
@Field
private String name;
@Field
private String tagline;
@Temporal(TemporalType.DATE)
private Date creationDate;
@ManyToOne
private Media avatar;
@ManyToOne
ChatGroup
ChatGroup is an entity that wraps the concept of a group. In a sense it’s similar to the User entity but has no phone
@Entity
@Indexed
public class ChatGroup {
@Id
private String id;
@Field
private String name;
@Field
private String tagline;
@Temporal(TemporalType.DATE)
private Date creationDate;
@ManyToOne
private Media avatar;
@ManyToOne
ChatGroup
All of these properties, the id, name, tagline, creation date and even avatar are a part of a group.
@Temporal(TemporalType.DATE)
private Date creationDate;
@ManyToOne
private Media avatar;
@ManyToOne
private User createdBy;
@OneToMany
@OrderBy("name ASC")
private Set<User> admins;
@OneToMany
@OrderBy("name ASC")
private Set<User> members;
public ChatGroup() {
id = UUID.randomUUID().toString();
}
ChatGroup
However, unlike a regular user a group is effectively created by a user
@Temporal(TemporalType.DATE)
private Date creationDate;
@ManyToOne
private Media avatar;
@ManyToOne
private User createdBy;
@OneToMany
@OrderBy("name ASC")
private Set<User> admins;
@OneToMany
@OrderBy("name ASC")
private Set<User> members;
public ChatGroup() {
id = UUID.randomUUID().toString();
}
ChatGroup
A group also has two lists of group administrators and group members
package com.codename1.whatsapp.server.entities;
import org.springframework.data.repository.CrudRepository;
public interface ChatGroupRepository extends
CrudRepository<ChatGroup, String> {
}
ChatGroupRepository
The chat group repository is empty as this is a feature we didn’t finish
@Entity
@Indexed
public class ChatMessage {
@Id
private String id;
@ManyToOne
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
ChatMessage
A chat message must be stored on servers for a while. If your device isn’t connected at this moment (e.g. flight) the server would need to keep the message until you’re
available again. It can then be purged. If we wish to be very secure we can encrypt the message based on a client public key, that way no one in the middle could “peek”
into the message. This should be easy enough to implement but I didn’t get around to do it, this would mostly be client side work. Here we store messages so they can
be fetched by the app.

Like everything else we have a unique string id per message
@Entity
@Indexed
public class ChatMessage {
@Id
private String id;
@ManyToOne
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
ChatMessage
Every message naturally has an author of that message
@Entity
@Indexed
public class ChatMessage {
@Id
private String id;
@ManyToOne
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
ChatGroupRepository
But more importantly a destination which can be either a different user or a group. Not both…
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
private boolean ack;
@OneToMany
@OrderBy("date ASC")
private Set<Media> attachments;
public ChatMessage() {
id = UUID.randomUUID().toString();
}
ChatMessage
Every message has a date time stamp for when it was sent
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
private boolean ack;
@OneToMany
@OrderBy("date ASC")
private Set<Media> attachments;
public ChatMessage() {
id = UUID.randomUUID().toString();
}
ChatMessage
The message has a text body alway, it can be null though
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
private boolean ack;
@OneToMany
@OrderBy("date ASC")
private Set<Media> attachments;
public ChatMessage() {
id = UUID.randomUUID().toString();
}
ChatMessage
If we have media attachments to the message
private User author;
@ManyToOne
private User sentTo;
@ManyToOne
private ChatGroup sentToGroup;
@Temporal(TemporalType.TIMESTAMP)
private Date messageTime;
private String body;
private boolean ack;
@OneToMany
@OrderBy("date ASC")
private Set<Media> attachments;
public ChatMessage() {
id = UUID.randomUUID().toString();
}
ChatMessage
The ack flag indicates whether the client acknowledged receiving the message. This works great for one-to-one messages but a message sent to a group would probably
need a more elaborate ack implementation.
@OneToMany
@OrderBy("date ASC")
private Set<Media> attachments;
public ChatMessage() {
id = UUID.randomUUID().toString();
}
public MessageDAO getDAO() {
String[] a;
if(attachments != null && !attachments.isEmpty()) {
a = new String[attachments.size()];
Iterator<Media> i = attachments.iterator();
for(int iter = 0 ; iter < a.length ; iter++) {
a[iter] = i.next().getId();
}
} else {
a = new String[0];
}
return new MessageDAO(id, author.getId(),
sentTo != null ? sentTo.getId() : sentToGroup.getId(),
messageTime, body, a);
}
ChatMessage
The DAO is again practically copied from the facebook app, we can include the id’s for the attachments as part of the message
package com.codename1.whatsapp.server.entities;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
public interface ChatMessageRepository extends
CrudRepository<ChatMessage, String> {
@Query("select n from ChatMessage n where n.ack = false and "
+ "n.sentTo.id = ?1 order by messageTime asc")
public List<ChatMessage> findByUnAcked(String sentTo);
}
ChatMessageRepository
The chat message repository includes one find method which helps us find messages that we didn’t acknowledge yet. If a specific user has pending messages this
finder is used to locate such messages and send them again.
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class MessageDAO {
private String id;
private String authorId;
private String sentTo;
@JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSS")
private Date time;
private String body;
private String[] attachments;
public MessageDAO() {
}
public MessageDAO(String id, String authorId, String sentTo,
Date time, String body, String[] attachments) {
this.id = id;
this.authorId = authorId;
this.sentTo = sentTo;
this.time = time;
this.body = body;
MessageDAO
The MessageDAO entry represents the current message. It maps pretty accurately to the entity object.

With that we are effectively done with the entity and DAO layers. There is still an ErrorDAO but it’s effectively identical to what we had in the facebook app and is totally
trivial so I’ll skip that.

Creating a Whatsapp Clone - Part XII - Transcript.pdf

  • 1.
    Creating a WhatsAppClone - Part XII Lets continue with the other entities. I’ll skip media as it’s just a copy and paste of the facebook media class and partially implemented to boot.
  • 2.
    @Entity @Indexed public class ChatGroup{ @Id private String id; @Field private String name; @Field private String tagline; @Temporal(TemporalType.DATE) private Date creationDate; @ManyToOne private Media avatar; @ManyToOne ChatGroup ChatGroup is an entity that wraps the concept of a group. In a sense it’s similar to the User entity but has no phone
  • 3.
    @Entity @Indexed public class ChatGroup{ @Id private String id; @Field private String name; @Field private String tagline; @Temporal(TemporalType.DATE) private Date creationDate; @ManyToOne private Media avatar; @ManyToOne ChatGroup All of these properties, the id, name, tagline, creation date and even avatar are a part of a group.
  • 4.
    @Temporal(TemporalType.DATE) private Date creationDate; @ManyToOne privateMedia avatar; @ManyToOne private User createdBy; @OneToMany @OrderBy("name ASC") private Set<User> admins; @OneToMany @OrderBy("name ASC") private Set<User> members; public ChatGroup() { id = UUID.randomUUID().toString(); } ChatGroup However, unlike a regular user a group is effectively created by a user
  • 5.
    @Temporal(TemporalType.DATE) private Date creationDate; @ManyToOne privateMedia avatar; @ManyToOne private User createdBy; @OneToMany @OrderBy("name ASC") private Set<User> admins; @OneToMany @OrderBy("name ASC") private Set<User> members; public ChatGroup() { id = UUID.randomUUID().toString(); } ChatGroup A group also has two lists of group administrators and group members
  • 6.
    package com.codename1.whatsapp.server.entities; import org.springframework.data.repository.CrudRepository; publicinterface ChatGroupRepository extends CrudRepository<ChatGroup, String> { } ChatGroupRepository The chat group repository is empty as this is a feature we didn’t finish
  • 7.
    @Entity @Indexed public class ChatMessage{ @Id private String id; @ManyToOne private User author; @ManyToOne private User sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; ChatMessage A chat message must be stored on servers for a while. If your device isn’t connected at this moment (e.g. flight) the server would need to keep the message until you’re available again. It can then be purged. If we wish to be very secure we can encrypt the message based on a client public key, that way no one in the middle could “peek” into the message. This should be easy enough to implement but I didn’t get around to do it, this would mostly be client side work. Here we store messages so they can be fetched by the app. Like everything else we have a unique string id per message
  • 8.
    @Entity @Indexed public class ChatMessage{ @Id private String id; @ManyToOne private User author; @ManyToOne private User sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; ChatMessage Every message naturally has an author of that message
  • 9.
    @Entity @Indexed public class ChatMessage{ @Id private String id; @ManyToOne private User author; @ManyToOne private User sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; ChatGroupRepository But more importantly a destination which can be either a different user or a group. Not both…
  • 10.
    private User author; @ManyToOne privateUser sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; private boolean ack; @OneToMany @OrderBy("date ASC") private Set<Media> attachments; public ChatMessage() { id = UUID.randomUUID().toString(); } ChatMessage Every message has a date time stamp for when it was sent
  • 11.
    private User author; @ManyToOne privateUser sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; private boolean ack; @OneToMany @OrderBy("date ASC") private Set<Media> attachments; public ChatMessage() { id = UUID.randomUUID().toString(); } ChatMessage The message has a text body alway, it can be null though
  • 12.
    private User author; @ManyToOne privateUser sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; private boolean ack; @OneToMany @OrderBy("date ASC") private Set<Media> attachments; public ChatMessage() { id = UUID.randomUUID().toString(); } ChatMessage If we have media attachments to the message
  • 13.
    private User author; @ManyToOne privateUser sentTo; @ManyToOne private ChatGroup sentToGroup; @Temporal(TemporalType.TIMESTAMP) private Date messageTime; private String body; private boolean ack; @OneToMany @OrderBy("date ASC") private Set<Media> attachments; public ChatMessage() { id = UUID.randomUUID().toString(); } ChatMessage The ack flag indicates whether the client acknowledged receiving the message. This works great for one-to-one messages but a message sent to a group would probably need a more elaborate ack implementation.
  • 14.
    @OneToMany @OrderBy("date ASC") private Set<Media>attachments; public ChatMessage() { id = UUID.randomUUID().toString(); } public MessageDAO getDAO() { String[] a; if(attachments != null && !attachments.isEmpty()) { a = new String[attachments.size()]; Iterator<Media> i = attachments.iterator(); for(int iter = 0 ; iter < a.length ; iter++) { a[iter] = i.next().getId(); } } else { a = new String[0]; } return new MessageDAO(id, author.getId(), sentTo != null ? sentTo.getId() : sentToGroup.getId(), messageTime, body, a); } ChatMessage The DAO is again practically copied from the facebook app, we can include the id’s for the attachments as part of the message
  • 15.
    package com.codename1.whatsapp.server.entities; import java.util.List; importorg.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; public interface ChatMessageRepository extends CrudRepository<ChatMessage, String> { @Query("select n from ChatMessage n where n.ack = false and " + "n.sentTo.id = ?1 order by messageTime asc") public List<ChatMessage> findByUnAcked(String sentTo); } ChatMessageRepository The chat message repository includes one find method which helps us find messages that we didn’t acknowledge yet. If a specific user has pending messages this finder is used to locate such messages and send them again.
  • 16.
    import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; publicclass MessageDAO { private String id; private String authorId; private String sentTo; @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSS") private Date time; private String body; private String[] attachments; public MessageDAO() { } public MessageDAO(String id, String authorId, String sentTo, Date time, String body, String[] attachments) { this.id = id; this.authorId = authorId; this.sentTo = sentTo; this.time = time; this.body = body; MessageDAO The MessageDAO entry represents the current message. It maps pretty accurately to the entity object.
 With that we are effectively done with the entity and DAO layers. There is still an ErrorDAO but it’s effectively identical to what we had in the facebook app and is totally trivial so I’ll skip that.