Saturday, August 3, 2013

Cross domain Request using jQuery and Java

There are situations when we need to make request to other domains and then execute a callback function. Let’s say you are on mysite.com and want to make a request to someapi.com. This is not allowed by most of the modern browsers due to Same origin policy.

Let’s consider an example of  an AJAX request to a cross domain server. We know jQuery provides response callback methods for it. You’ll notice that you are able to make the AJAX call, the server is receiving the request, but the response callback functions do not execute. There are a couple of ways to overcome this and make cross-domain requests possible.
  • JSONP callback.
  • Cross-origin resource sharing (CORS).

JSONP callback

According to Wikipedia, JSONP or "JSON with padding" is a communication technique used in JavaScript programs which run in Web browsers. The Same origin policy is applicable to all elements of a web page, except for the <script> tag. JSONP makes use of this open policy to retrieve arbitrary JavaScript code and not JSON. The JavaScript code is then evaluated by the JavaScript interpreter, and not parsed by a JSON parser.

A JavaScript function to make JSONP calls:
The server has to send a response, which when evaluated by the JavaScript interpreter calls the ‘callback’ function.

String jsonpCall = request.getParameter("callback");
out.println(jsonpCall + "('success')"); // where out = response.getWriter();
// Reponse => callback('success')


There are some disadvantages of JSONP:-
  • As you see the callback is called dynamically and there are no events that handle the callback, all errors have to be handled manually. Since, jqXHR.success(), jqXHR.error()wont work. The best way described to do this in many forums is to have an timeout. Which in any case is not an 100% solution.
  • It only supports GET request.

Cross-origin resource sharing (CORS)

CORS is a mechanism in which the browser and the server can interact to determine whether or not to allow the cross-origin request. Details.
In this process, we just need to allow a particular origin to send cross domain request. We do this by adding  a response header to the response. After, adding the header it works like any other request of the same domain. Thus, it supports all jqXHR(jQuery XMLHttpRequest) response callback methods. It also supports POST request.

// If you want to allow http://192.168.1.2:8080/BlogExamples/origin.html
response.addHeader("Access-Control-Allow-Origin", 
    "http://192.168.1.2:8080/BlogExamples/origin.html");
// or to allow all origins.
response.addHeader("Access-Control-Allow-Origin", "*");

The only problem with this approach it requires you to have access to the server code.

Putting it all together:-

Client code: origin.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script src="jquery-2.0.3.min.js"></script>
<title>Origin</title>
</head>
<body>
    <button name="jsonCallback" onclick="jsonp()">JSONP</button>
    <button name="cors" onclick="cors()">CORS</button>
    <script type="text/javascript">
        function jsonp() {
            $.ajax({
                url : 'http://localhost:8080/BlogExamples/ResourceServlet',
                dataType : 'jsonp',
                jsonpCallback : 'callback',
                data : {
                    method : 'jsonp',
//                    jsonpCallback : 'callback'
                }
            });
        }

        // JSONP Callback
        function callback(response) {
            alert("Response from cross origin: " + response);
        }
        function cors() {
            $.ajax({
                url : 'http://localhost:8080/BlogExamples/ResourceServlet',
                type : 'POST',
                data : {
                    method : 'cors'
                },
                success : function(response) {
                    alert("Response from cross origin: " + response);
                },
                error : function(jqXHR, textStatus, error) {
                    alert(error);
                }
            });
        }
    </script>
</body>
</html>

Server code: ResourceServlet.java
package com.web;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class ResourceServlet
*/
@WebServlet("/ResourceServlet")
public class ResourceServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        process(request, response);
    }
protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        process(request, response);
    }
protected void process(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // Checks the the AJAX function calling
        if ("jsonp".equals(request.getParameter("method")))
            callbackResponse(request, response);
        else
            corsResponse(request, response);
    }
private void callbackResponse(HttpServletRequest request,
                HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charset=UTF-8");
        String jsonpCall = request.getParameter("callback");
        PrintWriter out = response.getWriter();

        try {       
            out.println(jsonpCall + "('success')");
        } catch (Exception e) {
            out.println(jsonpCall + "('error')");
        } finally {
            out.flush();
            out.close();
        }
    }

    private void corsResponse(HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        response.setContentType("text/html;charset=UTF-8");
        response.addHeader("Access-Control-Allow-Origin", "*");
        PrintWriter out = response.getWriter();
        try {
            out.println("'success'");
        } catch (Exception e) {
            out.println("'error'");
        } finally {
            out.flush();
            out.close();
        }   
    }
}

Sunday, June 23, 2013

HTTP Session Listeners

Event Listeners enable you to track key events in your Web applications.

In Java EE 6 typically there are 3 levels/types of Listeners:
  • Servlet Context
  • HTTP Session
  • Servlet Request
In this post we'll learn about HTTP Session Listeners. All events which are related to sessions are handled by session Listeners. The table below lists the Event and the corresponding Listener interfaces.

Events Listener Interface
Lifecycle javax.servlet.http.HttpSessionListener
Attribute Change javax.servlet.http.HttpSessionAttributeListener
Session Migration javax.servlet.http.HttpSessionActivationListener
Object Binding javax.servlet.http.HttpSessionBindingListener

Session Migration is itself a big topic and will be discussed in another post.

Lifecycle
This deals with the Lifecycle of a session, i.e, session creation and destruction.

package com.web;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener // Declares class as a Web Listener
public class SessionLifiecycle implements HttpSessionListener {

   /**
    * Called on Server startup
    */
    public SessionLifiecycle() {
        System.out.println("Listener Ready!");
    }

    /**
     * Called when session is created, 
     * for e.g a call to request.getSession();
     */
    public void sessionCreated(HttpSessionEvent sessionEvent) {
        System.out.println("Created: " + sessionEvent.getSession().getId());
    }

    /**
     * Called when session is destroyed, 
     * for e.g a call to request.getSession().invalidate();
     */
    public void sessionDestroyed(HttpSessionEvent sessionEvent) {
        System.out.println("Destroyed: " + sessionEvent.getSession().getId());
    } 
}

Output
Listener Ready!

Created: 325C3E174A7AD4DE8163544AD1E767ED
Destroyed: 325C3E174A7AD4DE8163544AD1E767ED

Attribute Change
This deals with change in session attributes. Event is fired when a attribute is added, replaced or removed.

package com.web;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

@WebListener
public class SessionAttributes implements HttpSessionAttributeListener {

    /**
     * Called on Server startup
     */
    public SessionAttributes() {
     System.out.println("Attribute Listener Ready!");
    }

    /**
     * Called when session attribute is added,
     * request.getSession().setAttribute("attr1", "1");
     */
    public void attributeAdded(HttpSessionBindingEvent se) {
     System.out.println("Attribute Added: " + se.getName());
    }

    /**
     * Called when session attribute is replaced, value of existing attribute is changed
     * request.getSession().setAttribute("attr1", "2"); 
     */
    public void attributeReplaced(HttpSessionBindingEvent se) {
     System.out.println("Attribute Replaced: " + se.getName());
    }
    
    /**
     * Called when session attribute is removed, 
     * request.getSession().removeAttribute("attr1");
     */
    public void attributeRemoved(HttpSessionBindingEvent se) {
     System.out.println("Attribute Removed: " + se.getName());
    }
}

Output
Attribute Listener Ready!

Attribute Added: attr1; value: 1
Attribute Replaced: attr1; value: 1       // Value changes only after listener execution
Attribute Removed: attr1; value: 2

Object Binding
This notifies an object is being bound or unbound to/from a session.

package com.web;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

// @WebListener This is not required
public class SessionObjectBinding implements HttpSessionBindingListener {

    public SessionObjectBinding() {
        System.out.println("Binding Object created!");
    }

    /**
     * Set the value of this object
     */
    @Override
    public String toString() {
        return "Constant value";
    }

    /**
     * Called when this object is bound to a session.
     * request.getSession().setAttribute("object1", new SessionObjectBinding());
     */
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("Value Unbound: " + event.getName() + "; Value: "
                + event.getValue());
    }

    /**
     * Called when this object is unbound from a session.
     * request.getSession().removeAttribute("object1");
     */
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("Value Bound: " + event.getName() + "; Value: "
                + event.getValue());
    }
}

Output
When an object is bound to the session an attribute is also added so attribute Listener is also called.
Binding Object created!

Value Bound: object1; Value: Constant value
Attribute Added: object1; value: Constant value

Value Unbound: object1; Value: Constant value
Attribute Removed: object1; value: Constant value