Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Thursday, June 5, 2014

A chat application with WebSockets, Java and jQuery



I'm not going to describe the WebSockets protocol here - you can find zillions of tutorials online. In this short article I show how to build a simple chat application based on WebSockets.

The good news is that you don't have to worry about the nitty-gritty details of parsing WebSockets headers and packets, that's already implemented by open source libraries. On top of those you can write a server in Java, C#, Python, etc.
I use Tyrus, which makes it so easy to implement the server side of my chat application.
First I need to create a host for my endpoint:

package server;

import java.io.BufferedReader;
import java.io.InputStreamReader;
 
import org.glassfish.tyrus.server.Server;
 
public class WebSocketServer {
 
    public static void main(String[] args) {
        runServer();
    }
 
    public static void runServer() {
        Server server = new Server("localhost", 8025, "/chat", ChatEndpoint.class);
 
        try {
            server.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.print("Please press a key to stop the server.");
            reader.readLine();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            server.stop();
        }
    }
}

Look at the line where I create the Server object: I'll deploy ChatEndpoint to localhost:8025/chat.
Now I need to create the server itself - this ChatEndpoint type:
package server;

import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
 
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.server.ServerEndpoint;
 
@ServerEndpoint(value = "/test")
public class ChatEndpoint {
 
    private Logger logger = Logger.getLogger(this.getClass().getName());
    // keep all open WebSocket sessions (from all users)
    private static Queue queue = new ConcurrentLinkedQueue<>();

    @OnOpen
    public void onOpen(Session session) {
        logger.info("Connect with session: " + session.getId());
        queue.add(session);
        logger.log(Level.INFO, "Connected with " +  session.getId());
    }
 
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.log(Level.INFO, "Mesage received " + message);
        try {
            // broadcast message to all open WebSocket sessions
            for (Session s : queue) {
             // include the original sender
             s.getBasicRemote().sendText(message);
             logger.log(Level.INFO, "Message sent: " + message);
            }
         } catch (IOException e) {
            logger.log(Level.INFO, e.toString());
         }
        // we could have a return here, in which case the returned string
        // would be the one sent from server to client,
        // as if doing s.getBasicRemote().sendText(message)
        //return message + "TEST";
    }
 
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
        queue.remove(session);
        logger.log(Level.INFO, "Connection closed with " + session.getId());
    }
}
Tyrus makes things easy for us. Look at the annotation @ServerEndpoint(value = "/test") - here I'm saying that my server is accessible under the relative path "/test" under the URL mentioned before, thus the whole URL becomes: localhost:8025/chat/test.

We have three more annotations:
@OnOpen to mark the method for opening the connection from a client to this server; here session uniquely identifies the connecting client
@OnClose to mark the callback triggered when the connection with a particular client closes
@OnMessage for the method that does the most important job: whenever a message is received from a client, this method broadcasts the message to all clients (including the sender)

That's all on the server side.
So now the client is a simple web page with some jQuery embellishments:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type='text/javascript' src='jquery.js'></script>
<script type='text/javascript' src='jquery-ui-1.8.23.custom.min.js'></script>
<script type='text/javascript'>
var wsocket;
function connect() {
   wsocket = new WebSocket("ws://localhost:8025/chat/test");
   wsocket.onmessage = onMessage;
   alert("Connect");
}

function onMessage(evt) {
   $("#chatText").append("\n" + evt.data);
}

$(document).ready(
  function() {
$("#connect").click(
function() {
wsocket = new WebSocket("ws://localhost:8025/chat/test");
wsocket.onmessage = onMessage;
}
);

$("#send").click(
function() {
wsocket.send($("#username").val() + ":" + $("#tosend").val());
$("#tosend").val("");
}
);
  }
);
</script>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Chat</title>
</head>
<body>
Username: <input type="text" id="username"><br>
<input type="submit" value="Connect" id="connect">
<input type="submit" value="Disconnect"><br><br>

<textarea rows="30" cols="50" id="chatText">
</textarea><br>
<textarea rows="4" cols="50" id="tosend">
</textarea>
<br>
<input type="submit" value="Send" id="send">

</body>
</html>


Note how we open the connection with the server:  new WebSocket("ws://localhost:8025/chat/test").
Sending messages from the client to the server is simple too: wsocket.send($("#username").val() + ":" + $("#tosend").val());

Incredibly easy, isn't it ? 
Oh, by the way, this is the list of jars I need for the application to compile (you can use Maven to simplify the deployment a bit):





Sunday, June 9, 2013

Sweat, blood and tears with JSF

I'll start with the conclusion: if you're building standard, textbook web applications with Java Server Faces, you're going to love the technology. But the moment you start developing things that are a bit off the beaten track, the fun begins .... Let's take for example the calendar I presented recently. I want to add it some more functionality: when clicking on a certain day, you're redirected to a new page where you can create a new appointment. Once you confirm it, you'll be returned to the calendar page. So, this is how I extended the jQuery calendar:


dayClick: function(date, allDay, jsEvent, view) {
     if (allDay) {
        alert('Clicked on the entire day: ' + date);
        var url = "http://localhost:8080/Patients/appointments.jsf?day=" + date.getDate() + "&month=" + (date.getMonth() + 1) + "&year=" + date.getFullYear();    
        $(location).attr('href',url);  
     } else {
        //alert('Clicked on the slot: ' + date);
     }

}
Note that I've hardcoded the path to the appointment page ("appointments.jsp"). More importantly, I'm passing the date (day, month, year) as URL request parameters, e.g.: http://localhost:8080/Patients/appointments.jsf?day=5&month=7&year=2013

On the appointments.jsp page, one of the things I do is to print the chosen date:
<%
out.println("Data: " + request.getParameter("day") + "-" + request.getParameter("month") + "-" + request.getParameter("year"));
%>
Then, I've added a text input and a button, so that the user can fill in a patient name (or part of it) and click the button in order to search (in a DB) for all patients with the name containing the given text. The patients are supposed to be displayed in a selectOneListBox, as shown below:
<h:inputText binding="#{PatientsSearch.inputText1}" valueChangeListener="#{PatientsSearch.lookForName}" immediate="true" id="inputText1"/>
<h:commandButton value="Patient search:" action="#{PatientsSearch.getPatientsByName}">
</h:commandButton>

<h:selectOneListbox id="patients" value="#{PatientsSearch.selectedPatient}">
<f:selectItems value="#{PatientsSearch.patients}" var="p"
       itemLabel="#{p.name}:#{p.phoneMobile}" itemValue="#{p.name}" />
</h:selectOneListbox>

Almost everything works fine, i.e. if you click the "Patient search" button, the listbox will indeed be filled in with the corresponding patients' names and thus you can select on of the names from the list. I'm saying "almost", because if you change your mind and look up another patient name, clicking the "Patient search" button doesn't produce any result (actually I debugged it and noticed that the handler PatientsSearch.getPatientsByName is indeed called, but somehow the listbox is not refreshed with the new names). If you don't select any name in the list and perform a new patient search, this will work (so clicking the button produces the desired result). It looks as if the listbox is not just a "select one", but "use only once": the moment you select an item from the list, you can't refresh it anymore ....
Anyway, I actually didn't like the list, so I've replaced it with a table:
<h:dataTable var="patient" value="#{PatientsSearch.patients}" style="width:300px;height:150px" rowClasses="oddRow, evenRow">
  <h:column id="name">
    <f:facet name="header">
    <h:outputText value="Name" />
    </f:facet>
    <h:outputText value="#{patient.name}" />
  </h:column>
  <h:column id="phone">
    <f:facet name="header">
    <h:outputText value="Phone" />
    </f:facet>
    <h:outputText value="#{patient.phoneMobile}" />
  </h:column>
  <h:column>
    <h:inputHidden id="patientId" value="#{patient.id}"/>
  </h:column>
</h:dataTable>
I want to show the patient's name in the first column and the patient's phone number in the second. Wait a minute, there are three columns .... The third column contains an inputHidden field, whose value is the patient's ID - patient.id. So, what's the deal with this one ? Well, when I click a row in this table, I want that patient to be selected. Since the patient's ID uniquely identifies each patient, I can't simply use the name (it might not be unique). So I'm going to keep the patient's ID in this hidden field and when the user selects a patient, this ID will be sent to the managed bean that takes care of creating the new appointment using the patient's ID. Compare this with the selectOneListbox above: when I was selecting a patient in the listbox, the selected Patient instance (as a Java type) was passed to the managed bean, as the field PatientsSearch.selectedPatient. So I'm losing this elegance when working with the table, but ok ....

Next, let's do what I've just said: when the user selects a patient from the list, the patient's ID needs to be sent to a backing bean (i.e. we need to preserve it in order to create the appointment later). Again, look how simple was to do that with the selectOneListbox: just add value="#{PatientsSearch.selectedPatient}" and you're done. This is the intrinsic functionality of the listbox. The table however is meant rather for listing data, such as the patients in our case, not necessarily for selecting it. After googling around for a while, I've arrived to the conclusion that all I need to do is:
1. use some jQuery/JavaScript functionality to select a row in the table (and highlight the selection)
2. use a JSF inputHidden field to keep the patient's ID of the selected row

These two steps are shown below. Note that I first reset the background of all "tr" (except for the first, which is the header of the table and thus excluded from any selection) and then I set the background of the selected row to orange.

After that I do step 2 - this single line of code cost me many hours of searches online: $("#j_id_id13\\:testId").val($(this).find('td:last').find('input').attr("value"));
Note that the id of the inputHidden is "testId", but JSF prefixes it with "j_id_id13" when generating the corresponding HTML input field - you can see that by showing the page source in the browser. "\\" are needed to escape ":". Then, $(this).find('td:last') means that I want to go to the last column (td) in the current row (this). Finally, I look for "input" (the HTML field generated) and read its "value" attribute - this is the selected patient's ID.
Another important note: I've displayed the value of the just set hidden field in an "alert", since viewing the browser source doesn't show the hidden field being updated.

$("tr").not(':first').click(
    function () {
      $(this).parent().find("tr").css("background", "");
      $(this).css("background","orange");
      $("#j_id_id13\\:testId").val($(this).find('td:last').find('input').attr("value"));
      alert($("#j_id_id13\\:testId").val());
    }
);

<h:inputHidden id="testId"/>

3. once I have selected the patient (a row in the table) and I click the button "Create appointment", I can retrieve the patient's ID from the hidden field with the following handler in the backing bean:
FacesContext context = FacesContext.getCurrentInstance();  
Map requestMap = context.getExternalContext().getRequestParameterMap();  
String value = (String)requestMap.get("j_id_id13:testId");
        
Patient patient;
if (value != null) {
    patient = getPatientById(value);
}
return "toCalendar";

Now I'm almost done. "Almost" again. Remember I'm on the page "appointments.jsp" and here I click "Patient search". What happens is that I'm basically sent back to the same page in the browser, but then the date line I've shown at the beginning will only display nulls:
<%
out.println("Data: " + request.getParameter("day") + "-" + request.getParameter("month") + "-" + request.getParameter("year"));
%>
That happens because when clicking the button, I'm actually creating a new request for this page, losing thus the URL parameters encoded from the calendar page. So, here's what I can do to preserve the date:
1. write the date into a cookie
2. use again hidden fields to pass the date around
3. do URL rewriting with an onclick JavaScript handler when clicking the "Patient search" button
4. use some form of browser storage (see HTML5, etc.)

And the ideas could just flow like crazy hadn't I found the simplest method:
<h:commandButton value ="Patient search:" action="#{PatientsSearch.getPatientsByName}">
    <f:param name="day" value="#{request.getParameter('day')}" />
    <f:param name="month" value="#{request.getParameter('month')}" />
    <f:param name="year" value="#{request.getParameter('year')}" />  
</h:commandButton>
Note here the EL expressions used to transmit the request parameters to the f:param and thus keep them in the request scope.
That's all folks ! Pffffffffff ....

P.S. I know you'll say there are many JSF plugins which can implement this easier, but sometimes I'm just too conservative ....

Sunday, March 3, 2013

Calendar with data from database: jQuery + servlets + Ajax


I have a Java application that reads appointment data from a database and then shows them in a Java Swing UI. Now I want to publish this application online.
After a bit of research, I've found this convenient jQuery calendar and decided to use it. The main question is  then how to use my existing Java code that reads appointments and expose them to this calendar. So, what I've done:

1. created a Java servlet that reads the database







2. created the jQuery fullcalendar that invokes this servlet



Note here the "url: agenda", which basically means I'm calling the servlet deployed at /agenda.

3. then I realized that I wanted to be able to react to month/week/day changes: whenever I click the previous and next buttons, I should pass the new time period to the servlet, retrieve the new appointments from the DB and show them in the same calendar; this is how I've achieved this: