I am trying to implement real-time notification system using Servlet and ServerSentEvents
This is the following code I have implemented
@WebServlet("/notification")public class NotificationSSE extends HttpServlet { @Override public void init() throws ServletException { this.getServletContext().setAttribute("is_message", new Object()); super.init(); } @Override protected void doGet(HttpServletRequest reuest, HttpServletResponse response) throws ServletException, IOException { Object obj = null; try { obj = this.getServletContext().getAttribute("is_message"); response.setContentType("text/event-stream"); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); MessagesHolder.users.add(response); synchronized (obj) { obj.wait(); } } catch (Exception e) { e.printStackTrace(); } }
This is the MessageHolder which will be responsible to send message
I have a plan to change the Vector to a HashMap which would link the user and send the Message only to the particular user
public class MessagesHolder { public static Vector<HttpServletResponse> users = new Vector<>(); public void sendMessage(Message message) { try { for (HttpServletResponse user : users) { PrintWriter writer = user.getWriter(); writer.write("data: " + message.toJson() +"\n\n"); writer.flush(); } } catch (Exception e) { e.printStackTrace(); } }}
This is a servlet which is responsible for receiving the messages post by the user and stores it in the database
@WebServlet("/send-message")public class SendMessage extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { String messageSubject = req.getParameter("message_subject"); String messageContent = req.getParameter("message_content"); String messageSendTime = req.getParameter("message_sendTime"); String[] receivers = req.getParameterValues("receivers[]"); String user_id = req.getSession().getAttribute("user_id").toString(); try ( Connection conn = DriverManager.getConnection(DBUtil.getUrl(), DBUtil.getUser(), DBUtil.getPassword()); PreparedStatement insertMessage = conn.prepareStatement("INSERT INTO Message(message_title, message_content, sender_id, send_time) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); PreparedStatement insertNotify = conn .prepareStatement("INSERT INTO Notification (message_id, receiver_id) VALUES (?,?)"); PrintWriter out = resp.getWriter();) { insertMessage.setString(1, messageSubject); insertMessage.setString(2, messageContent); insertMessage.setString(3, user_id); insertMessage.setString(4, messageSendTime.split("T")[0] +" " + messageSendTime.split("T")[1] +":00"); insertMessage.executeUpdate(); Message message = new Message(user_id, messageSubject, messageContent); ResultSet generatedKeys = insertMessage.getGeneratedKeys(); if (generatedKeys.next()) { int i = 0; Vector<String> receiverList = new Vector<>(); long messageId = generatedKeys.getLong(1); insertNotify.setLong(1, messageId); while (i < receivers.length) { insertNotify.setString(2, receivers[i]); insertNotify.addBatch(); receiverList.add(receivers[i]); i++; } insertNotify.executeBatch(); out.write("sent succssfully"); MessagesHolder messagesHolder = new MessagesHolder(); messagesHolder.sendMessage(message); out.flush(); } } catch (Exception ex) { ex.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } }}
The Message Object
public class Message { public String senderId; public String title; public String message; public Status status; public Message(String senderId, String title, String message) { this.senderId = senderId; this.title = title; this.message = message; this.status = Status.SENT; } @Override public String toString() { return "Message [senderId=" + senderId +", title=" + title +", message=" + message +", status=" + status+"]"; } public JSONObject toJson(){ JSONObject obj = new JSONObject(); obj.put("senderId", senderId); obj.put("title", title); obj.put("message", message); obj.put("status", status); return obj; }}
Client Side js
let eventSource = new EventSource("notification");const list = document.querySelector("ul");eventSource.onopen = (event) => { list.append("Connection created"); };eventSource.onerror = (event) => { console.log("Error occured"); console.log(event);};eventSource.onmessage = function (event) { let listItem = document.createElement("li"); listItem.innerText = event.data; list.append(listItem); console.log(event, event.data);};
Is there any better way to implement this because I read that It's not recommended to use wait() inside request
What are other ways I could implement the server sent events to achieve the notification sending.
I don't know much about Http2 keep-alive connection, Is there any possibility with that I can create a channel send the event streams with that
Also need a clarification that is there a possibility that could I implement this with ServletListeners by any chance and send the notification