"Impact of front-end architecture on development cost", Viktor Turskyi
Creating a Whatsapp Clone - Part XIII - Transcript.pdf
1. Creating a WhatsApp Clone - Part XIII
The next step is the services layer which implements the relatively simple business layer of this application
2. @Service
public class UserService {
@Autowired
private UserRepository users;
@Autowired
private ChatGroupRepository groups;
@Autowired
private ChatMessageRepository messages;
@Autowired
private APIKeys keys;
@Autowired
private PasswordEncoder encoder;
@Autowired
private MediaRepository medias;
e
UserService
We’ll start with the UserService class, it’s the main class in this package and handles pretty much the entire business logic of the app
It’s a service bean, this means it handles generic business logic for the application
3. @Service
public class UserService {
@Autowired
private UserRepository users;
@Autowired
private ChatGroupRepository groups;
@Autowired
private ChatMessageRepository messages;
@Autowired
private APIKeys keys;
@Autowired
private PasswordEncoder encoder;
@Autowired
private MediaRepository medias;
e
UserService
We need access to the repositories we just defined for users, groups and messages
4. @Service
public class UserService {
@Autowired
private UserRepository users;
@Autowired
private ChatGroupRepository groups;
@Autowired
private ChatMessageRepository messages;
@Autowired
private APIKeys keys;
@Autowired
private PasswordEncoder encoder;
@Autowired
private MediaRepository medias;
e
UserService
The API keys service is the exact one I used in facebook with the exception of a different properties file name. I’ll discuss that later, it generally abstracts API keys and
separates them from the source code
5. @Service
public class UserService {
@Autowired
private UserRepository users;
@Autowired
private ChatGroupRepository groups;
@Autowired
private ChatMessageRepository messages;
@Autowired
private APIKeys keys;
@Autowired
private PasswordEncoder encoder;
@Autowired
private MediaRepository medias;
e
UserService
The password encoder is used to encrypt and verify the token
6. private UserRepository users;
@Autowired
private ChatGroupRepository groups;
@Autowired
private ChatMessageRepository messages;
@Autowired
private APIKeys keys;
@Autowired
private PasswordEncoder encoder;
@Autowired
private MediaRepository medias;
@Autowired
private NotificationService notifications;
private void sendActivationSMS(String number, String text) {
Twilio.init(keys.get("twilio.sid"), keys.get("twilio.auth"));
Message message = Message
e
UserService
MediaRepository and NotificationService are identical to the stuff we had in the facebook clone app
7. private PasswordEncoder encoder;
@Autowired
private MediaRepository medias;
@Autowired
private NotificationService notifications;
private void sendActivationSMS(String number, String text) {
Twilio.init(keys.get("twilio.sid"), keys.get("twilio.auth"));
Message message = Message
.creator(new PhoneNumber(number),
new PhoneNumber(keys.get("twilio.phone")),
text)
.create();
message.getSid();
}
public UserDAO login(String phone, String auth)
throws LoginException {
List<User> userList;
userList = users.findByPhone(phone);
if (userList != null && userList.size() == 1) {
e
UserService
This method sends an SMS message via the twilio web service. If you recall we added the twilio SDK into the pom file in the first lesson. This SDK makes sending an
SMS message very easy as you can see from this code.
8. .creator(new PhoneNumber(number),
new PhoneNumber(keys.get("twilio.phone")),
text)
.create();
message.getSid();
}
public UserDAO login(String phone, String auth)
throws LoginException {
List<User> userList;
userList = users.findByPhone(phone);
if (userList != null && userList.size() == 1) {
User u = userList.get(0);
if (encoder.matches(auth, u.getAuthtoken())) {
return u.getLoginDAO();
}
throw new LoginException("Authentication error!");
}
throw new LoginException("User not found!");
}
private String createVerificationCode(int length) {
StringBuilder k = new StringBuilder();
Random r = new Random();
e
UserService
The login API lets us validate a user and get the current data the server has for that user. Since there is no username/password we need to use a token to authenticate
9. .creator(new PhoneNumber(number),
new PhoneNumber(keys.get("twilio.phone")),
text)
.create();
message.getSid();
}
public UserDAO login(String phone, String auth)
throws LoginException {
List<User> userList;
userList = users.findByPhone(phone);
if (userList != null && userList.size() == 1) {
User u = userList.get(0);
if (encoder.matches(auth, u.getAuthtoken())) {
return u.getLoginDAO();
}
throw new LoginException("Authentication error!");
}
throw new LoginException("User not found!");
}
private String createVerificationCode(int length) {
StringBuilder k = new StringBuilder();
Random r = new Random();
e
UserService
First we need to find the user with the given phone. Assuming the user isn’t there we’ll throw an exception
10. .creator(new PhoneNumber(number),
new PhoneNumber(keys.get("twilio.phone")),
text)
.create();
message.getSid();
}
public UserDAO login(String phone, String auth)
throws LoginException {
List<User> userList;
userList = users.findByPhone(phone);
if (userList != null && userList.size() == 1) {
User u = userList.get(0);
if (encoder.matches(auth, u.getAuthtoken())) {
return u.getLoginDAO();
}
throw new LoginException("Authentication error!");
}
throw new LoginException("User not found!");
}
private String createVerificationCode(int length) {
StringBuilder k = new StringBuilder();
Random r = new Random();
e
UserService
Since the auth is hashed as we discussed before, we need to test the incoming auth via the matches method in the encoder. It verifies the hash matches the auth token
11. if (encoder.matches(auth, u.getAuthtoken())) {
return u.getLoginDAO();
}
throw new LoginException("Authentication error!");
}
throw new LoginException("User not found!");
}
private String createVerificationCode(int length) {
StringBuilder k = new StringBuilder();
Random r = new Random();
for (int iter = 0; iter < length; iter++) {
k.append(r.nextInt(10));
}
return k.toString();
}
public UserDAO signup(UserDAO user) throws SignupException {
List<User> ul = users.findByPhone(user.getPhone());
if (ul != null && ul.size() > 0) {
throw new SignupException(
"The phone number is already registered!");
e
UserService
This method creates a string of the given length which includes a random number for verification
12. return k.toString();
}
public UserDAO signup(UserDAO user) throws SignupException {
List<User> ul = users.findByPhone(user.getPhone());
if (ul != null && ul.size() > 0) {
throw new SignupException(
"The phone number is already registered!");
}
User u = new User();
setProps(user, u);
u.setAuthtoken(UUID.randomUUID().toString());
u.setVerificationCode(createVerificationCode(4));
users.save(u);
sendActivationSMS(user.getPhone(), "Activation key: " + u.
getVerificationCode());
return u.getLoginDAO();
}
e
UserService
Signup creates an entry for a specific user but does’t activate the account until it’s verified
13. return k.toString();
}
public UserDAO signup(UserDAO user) throws SignupException {
List<User> ul = users.findByPhone(user.getPhone());
if (ul != null && ul.size() > 0) {
throw new SignupException(
"The phone number is already registered!");
}
User u = new User();
setProps(user, u);
u.setAuthtoken(UUID.randomUUID().toString());
u.setVerificationCode(createVerificationCode(4));
users.save(u);
sendActivationSMS(user.getPhone(), "Activation key: " + u.
getVerificationCode());
return u.getLoginDAO();
}
e
UserService
We first check if the phone number is already registered. If so we need to fail
14. return k.toString();
}
public UserDAO signup(UserDAO user) throws SignupException {
List<User> ul = users.findByPhone(user.getPhone());
if (ul != null && ul.size() > 0) {
throw new SignupException(
"The phone number is already registered!");
}
User u = new User();
setProps(user, u);
u.setAuthtoken(UUID.randomUUID().toString());
u.setVerificationCode(createVerificationCode(4));
users.save(u);
sendActivationSMS(user.getPhone(), "Activation key: " + u.
getVerificationCode());
return u.getLoginDAO();
}
e
UserService
Otherwise we create the new user and initialize the value of the data and the verification code
15. return k.toString();
}
public UserDAO signup(UserDAO user) throws SignupException {
List<User> ul = users.findByPhone(user.getPhone());
if (ul != null && ul.size() > 0) {
throw new SignupException(
"The phone number is already registered!");
}
User u = new User();
setProps(user, u);
u.setAuthtoken(UUID.randomUUID().toString());
u.setVerificationCode(createVerificationCode(4));
users.save(u);
sendActivationSMS(user.getPhone(), "Activation key: " + u.
getVerificationCode());
return u.getLoginDAO();
}
e
UserService
Finally we send the activation code and return the user entity
16. sendActivationSMS(user.getPhone(), "Activation key: " + u.
getVerificationCode());
return u.getLoginDAO();
}
public boolean verifyPhone(String userId, String code) {
User u = users.findById(userId).get();
if (u.getVerificationCode().equals(code)) {
u.setVerificationCode(null);
u.setVerified(true);
users.save(u);
return true;
}
return false;
}
private void setProps(UserDAO user, User u) {
u.setName(user.getName());
u.setTagline(user.getTagline());
}
public void update(String auth, UserDAO user) {
e
UserService
The verify method activates a user account, if the verification code is correct we mark the user as verified and return true.
17. sendActivationSMS(user.getPhone(), "Activation key: " + u.
getVerificationCode());
return u.getLoginDAO();
}
public boolean verifyPhone(String userId, String code) {
User u = users.findById(userId).get();
if (u.getVerificationCode().equals(code)) {
u.setVerificationCode(null);
u.setVerified(true);
users.save(u);
return true;
}
return false;
}
private void setProps(UserDAO user, User u) {
u.setName(user.getName());
u.setTagline(user.getTagline());
}
public void update(String auth, UserDAO user) {
e
UserService
We use setProps both from the signup and update methods. There isn’t much here but if we add additional meta-data this might become a bigger method like it is in the
facebook clone
18. }
private void setProps(UserDAO user, User u) {
u.setName(user.getName());
u.setTagline(user.getTagline());
}
public void update(String auth, UserDAO user) {
User u = users.findById(user.getId()).get();
if (encoder.matches(auth, u.getAuthtoken())) {
setProps(user, u);
users.save(u);
}
}
public byte[] getAvatar(String userId) {
User u = users.findById(userId).get();
if (u.getAvatar() != null) {
return u.getAvatar().getData();
}
return null;
}
public void setAvatar(String auth, String userId, String mediaId) {
e
UserService
Update verifies the users token then updates the properties. There isn’t much here
19. setProps(user, u);
users.save(u);
}
}
public byte[] getAvatar(String userId) {
User u = users.findById(userId).get();
if (u.getAvatar() != null) {
return u.getAvatar().getData();
}
return null;
}
public void setAvatar(String auth, String userId, String mediaId) {
Media m = medias.findById(mediaId).get();
User u = users.findById(userId).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setAvatar(m);
users.save(u);
}
}
public void userTyping(String userId, String toUser, boolean t) {
Optional<User> to = users.findById(toUser);
e
UserService
These aren’t used at the moment but they are pretty much identical to what we have in the facebook clone and should be easy to integrate in a similar way
20. User u = users.findById(userId).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setAvatar(m);
users.save(u);
}
}
public void userTyping(String userId, String toUser, boolean t) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendUserTyping(to.get().getAuthtoken(), userId, t);
} else {
ChatGroup g = groups.findById(toUser).get();
for(User u : g.getMembers()) {
AppSocket.sendUserTyping(u.getAuthtoken(), userId, t);
}
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
e
UserService
This is part of the work to integrate support for the “user typing" feature. Right now the client app doesn’t send or render this event but it should be relatively simple to
add. When a user starts typing to a conversation we can invoke this method
21. User u = users.findById(userId).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setAvatar(m);
users.save(u);
}
}
public void userTyping(String userId, String toUser, boolean t) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendUserTyping(to.get().getAuthtoken(), userId, t);
} else {
ChatGroup g = groups.findById(toUser).get();
for(User u : g.getMembers()) {
AppSocket.sendUserTyping(u.getAuthtoken(), userId, t);
}
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
e
UserService
toUser can be a user or a group is the user is present it’s a user
22. User u = users.findById(userId).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setAvatar(m);
users.save(u);
}
}
public void userTyping(String userId, String toUser, boolean t) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendUserTyping(to.get().getAuthtoken(), userId, t);
} else {
ChatGroup g = groups.findById(toUser).get();
for(User u : g.getMembers()) {
AppSocket.sendUserTyping(u.getAuthtoken(), userId, t);
}
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
e
UserService
I’ll discuss the event code in the sockets when we reach the app socket class
23. User u = users.findById(userId).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setAvatar(m);
users.save(u);
}
}
public void userTyping(String userId, String toUser, boolean t) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendUserTyping(to.get().getAuthtoken(), userId, t);
} else {
ChatGroup g = groups.findById(toUser).get();
for(User u : g.getMembers()) {
AppSocket.sendUserTyping(u.getAuthtoken(), userId, t);
}
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
e
UserService
If this is a group we need to send the event to all the users within the group via the socket connection
24. }
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
cm.setMessageTime(new Date());
Optional<User> to = users.findById(m.getSentTo());
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(m.getSentTo(), cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(), json,
m.getBody());
} else {
ChatGroup g = groups.findById(m.getSentTo()).get();
cm.setSentToGroup(g);
String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
e
UserService
This method sends a message to its destination which can be a user or a group.
25. }
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
cm.setMessageTime(new Date());
Optional<User> to = users.findById(m.getSentTo());
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(m.getSentTo(), cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(), json,
m.getBody());
} else {
ChatGroup g = groups.findById(m.getSentTo()).get();
cm.setSentToGroup(g);
String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
e
UserService
In order to send a message we first need to create a ChatMessage entity so we can persist the message in case delivery failed.
26. }
}
}
public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
cm.setMessageTime(new Date());
Optional<User> to = users.findById(m.getSentTo());
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(m.getSentTo(), cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(), json,
m.getBody());
} else {
ChatGroup g = groups.findById(m.getSentTo()).get();
cm.setSentToGroup(g);
String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
e
UserService
This is the same code we saw in the typing event. If the message is destined to a user the following block will occur
27. public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
cm.setMessageTime(new Date());
Optional<User> to = users.findById(m.getSentTo());
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(m.getSentTo(), cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(), json,
m.getBody());
} else {
ChatGroup g = groups.findById(m.getSentTo()).get();
cm.setSentToGroup(g);
String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
}
}
return cm.getDAO();
}
e
UserService
Otherwise we’ll go to the else block where the exact same code will execute in a loop over all the members of the group
28. public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
cm.setMessageTime(new Date());
Optional<User> to = users.findById(m.getSentTo());
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(m.getSentTo(), cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(), json,
m.getBody());
} else {
ChatGroup g = groups.findById(m.getSentTo()).get();
cm.setSentToGroup(g);
String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
}
}
return cm.getDAO();
}
e
UserService
We mark the destination of the message and convert it to a JSON string
29. public MessageDAO sendMessage(MessageDAO m) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById(m.getAuthorId()).get());
cm.setBody(m.getBody());
cm.setMessageTime(new Date());
Optional<User> to = users.findById(m.getSentTo());
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(m.getSentTo(), cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(), json,
m.getBody());
} else {
ChatGroup g = groups.findById(m.getSentTo()).get();
cm.setSentToGroup(g);
String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
}
}
return cm.getDAO();
}
e
UserService
We the invoke the send message API
30. String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
}
}
return cm.getDAO();
}
private void sendMessageImpl(String authToken, String pushKey,
String json, String message) {
if(!AppSocket.sendMessage(authToken, json)) {
notifications.sendPushNotification(pushKey, message, 1);
}
}
public void sendMessage(String toUser,
Map<String, Object> parsedJSON) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById((String)parsedJSON.get("authorId")).
get());
cm.setBody((String)parsedJSON.get("body"));
cm.setMessageTime(new Date());
Optional<User> to = users.findById(toUser);
e
UserService
Send message uses the socket to send the message to the device.
31. String json = createMessageImpl(m.getSentTo(), cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(), json,
m.getBody());
}
}
return cm.getDAO();
}
private void sendMessageImpl(String authToken, String pushKey,
String json, String message) {
if(!AppSocket.sendMessage(authToken, json)) {
notifications.sendPushNotification(pushKey, message, 1);
}
}
public void sendMessage(String toUser,
Map<String, Object> parsedJSON) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById((String)parsedJSON.get("authorId")).
get());
cm.setBody((String)parsedJSON.get("body"));
cm.setMessageTime(new Date());
Optional<User> to = users.findById(toUser);
e
UserService
If this failed and the device isn't reachable we should send this message as text using push notification
32. notifications.sendPushNotification(pushKey, message, 1);
}
}
public void sendMessage(String toUser,
Map<String, Object> parsedJSON) {
ChatMessage cm = new ChatMessage();
cm.setAuthor(users.findById((String)parsedJSON.get("authorId")).
get());
cm.setBody((String)parsedJSON.get("body"));
cm.setMessageTime(new Date());
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
cm.setSentTo(to.get());
String json = createMessageImpl(toUser, cm);
User usr = to.get();
sendMessageImpl(usr.getAuthtoken(), usr.getPushKey(),
json, cm.getBody());
} else {
ChatGroup g = groups.findById(toUser).get();
cm.setSentToGroup(g);
String json = createMessageImpl(toUser, cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(),
e
UserService
This method is identical to the other sendMessage method but it uses a JSON string which is more convenient when a message comes in through the websocket. The
previous version is the one used when this is invoked from the webservice (which is what we use) and this one works when a message is sent via the websocket
33. String json = createMessageImpl(toUser, cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(),
json, cm.getBody());
}
}
}
private String createMessageImpl(String toUser, ChatMessage cm) {
cm = messages.save(cm);
ObjectMapper objectMapper = new ObjectMapper();
try {
String dao = objectMapper.writeValueAsString(cm.getDAO());
return dao;
} catch(JsonProcessingException err) {
throw new RuntimeException(err);
}
}
public void sendJSONTo(String toUser, String json) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendMessage(to.get().getAuthtoken(), json);
e
UserService
This method converts a chat message entity to JSON so we can send it to the client.
34. String json = createMessageImpl(toUser, cm);
for(User u : g.getMembers()) {
sendMessageImpl(u.getAuthtoken(), u.getPushKey(),
json, cm.getBody());
}
}
}
private String createMessageImpl(String toUser, ChatMessage cm) {
cm = messages.save(cm);
ObjectMapper objectMapper = new ObjectMapper();
try {
String dao = objectMapper.writeValueAsString(cm.getDAO());
return dao;
} catch(JsonProcessingException err) {
throw new RuntimeException(err);
}
}
public void sendJSONTo(String toUser, String json) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendMessage(to.get().getAuthtoken(), json);
e
UserService
object mapper can convert a POJO object to the equivalent JSON string
35. String dao = objectMapper.writeValueAsString(cm.getDAO());
return dao;
} catch(JsonProcessingException err) {
throw new RuntimeException(err);
}
}
public void sendJSONTo(String toUser, String json) {
Optional<User> to = users.findById(toUser);
if(to.isPresent()) {
AppSocket.sendMessage(to.get().getAuthtoken(), json);
} else {
ChatGroup g = groups.findById(toUser).get();
for(User u : g.getMembers()) {
AppSocket.sendMessage(u.getAuthtoken(), json);
}
}
}
public UserDAO findRegisteredUser(String phone) {
return fromList(users.findByPhone(phone));
}
e
UserService
This method sends JSON via the socket to the group or a user. It allows us to propagate a message onward. It works pretty much like the other methods in this class that
send to group or user
36. for(User u : g.getMembers()) {
AppSocket.sendMessage(u.getAuthtoken(), json);
}
}
}
public UserDAO findRegisteredUser(String phone) {
return fromList(users.findByPhone(phone));
}
private UserDAO fromList(List<User> ul) {
if(ul.isEmpty()) {
return null;
}
User u = ul.get(0);
if(!u.isVerified()) {
return null;
}
return u.getDAO();
}
public UserDAO findRegisteredUserById(String id) {
return users.findById(id).get().getDAO();
}
e
UserService
This method finds the user matching the given phone number
37. for(User u : g.getMembers()) {
AppSocket.sendMessage(u.getAuthtoken(), json);
}
}
}
public UserDAO findRegisteredUser(String phone) {
return fromList(users.findByPhone(phone));
}
private UserDAO fromList(List<User> ul) {
if(ul.isEmpty()) {
return null;
}
User u = ul.get(0);
if(!u.isVerified()) {
return null;
}
return u.getDAO();
}
public UserDAO findRegisteredUserById(String id) {
return users.findById(id).get().getDAO();
}
e
UserService
This method is used by findRegisteredUser and findRegisteredUserById. It generalizes the translation of a user list to a single UserDAO value. It implicitly fails for
unverified users as well
38. public UserDAO findRegisteredUserById(String id) {
return users.findById(id).get().getDAO();
}
public void ackMessage(String id) {
ChatMessage m = messages.findById(id).get();
m.setAck(true);
messages.save(m);
}
public void sendUnAckedMessages(String toUser) {
List<ChatMessage> mess = messages.findByUnAcked(toUser);
for(ChatMessage m : mess) {
sendMessage(m.getDAO());
}
}
public void updatePushKey(String auth, String id, String key) {
User u = users.findById(auth).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setPushKey(key);
users.save(u);
}
e
UserService
Ack allows us to acknowledge that a message was received, it just toggles the ack flag.
39. public UserDAO findRegisteredUserById(String id) {
return users.findById(id).get().getDAO();
}
public void ackMessage(String id) {
ChatMessage m = messages.findById(id).get();
m.setAck(true);
messages.save(m);
}
public void sendUnAckedMessages(String toUser) {
List<ChatMessage> mess = messages.findByUnAcked(toUser);
for(ChatMessage m : mess) {
sendMessage(m.getDAO());
}
}
public void updatePushKey(String auth, String id, String key) {
User u = users.findById(auth).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setPushKey(key);
users.save(u);
}
e
UserService
When a user connects via websocket this method is invoked, it finds all the messages that weren't acked by the user and sends them to that user. That way if a device
lost connection it will get the content once it’s back online.
40. public void ackMessage(String id) {
ChatMessage m = messages.findById(id).get();
m.setAck(true);
messages.save(m);
}
public void sendUnAckedMessages(String toUser) {
List<ChatMessage> mess = messages.findByUnAcked(toUser);
for(ChatMessage m : mess) {
sendMessage(m.getDAO());
}
}
public void updatePushKey(String auth, String id, String key) {
User u = users.findById(auth).get();
if (encoder.matches(auth, u.getAuthtoken())) {
u.setPushKey(key);
users.save(u);
}
}
}
e
UserService
This method is invoked on launch to update the push key in the server so we can send push messages to the device. With that UserService is finished