Developer: Java
   DOWNLOAD
Download Oracle Database 10g Oracle JDeveloper 10g & ADF
Download Oracle Database 10g Sample code
 
   TAGS
ajax, java, All
 

Using EJB3 with Ajax


by Andrei Cioroianu

Learn how to build a Java EE application that uses Ajax, JavaServer Faces, and ADF Faces for the Web tier and EJB3 for the business logic.

Published March 2007

Enterprise applications can use Ajax to provide better Web interfaces that increase the user productivity. In many cases, it is possible to submit a partially completed form to obtain useful information from the server application. For example, the server could perform some early validation or it could use the partial user input to suggest values for the empty form fields, speeding up the data entry process. Ajax can also be used to connect to data feeds whose information is displayed without refreshing the whole page.

In this article, I'll present a simple application containing a Web page that uses Ajax to connect to an ad feed. The user input is submitted to a controller servlet that invokes a business method of an EJB component to select a personalized ad. The business method returns an entity that is used in a JSP page to generate the Ajax response, which is then inserted in the Web page, using DHTML. The following diagram depicts the application's architecture:



Figure 1


I'll rely on Oracle JDeveloper wizards for creating the application's components and user interface. You'll see how to create entities, session beans, Ajax controller servlets, JSP pages that generate Ajax responses, and JSF forms based on ADF Faces. In the last part of the article, you'll find reusable JavaScript functions for creating, initializing and deleting XMLHttpRequest objects. You'll also learn several Ajax techniques, such as using callback wrappers, submitting the form data with Ajax, and preventing memory leaks in the Web browser.

Creating EJB Components with JDeveloper

In this section, I'll use JDeveloper's EJB wizards to create a simple entity and an EJB session component whose business method will be invoked from an Ajax client via an Ajax-EJB controller. Launch JDeveloper and create a new project named ajaxejb.

Creating an entity

Right-click the newly created project in the Applications navigator and click New. In the New Gallery window, expand the Business Tier node in the left panel and select EJB. Then, select Entity (JPA/EJB 3.0) in the right panel of the same window and click OK:



Figure 2


Skip the Welcome page of the Create JPA / EJB 3.0 wizard and provide the AdEntity name of the Entity Class. The wizard will also change the Entity Name field:



Figure 3


The next step of the wizard lets you select an inheritance option. We'll use No inheritance for this first example:



Figure 4


The third step lets you enter a table name, select a schema, and provide the entity's superclass:



Figure 5


The fourth step allows you to change the default id and version fields. For our example, uncheck the Include @Version field option, change the name of the @Id field to keyword, and select the String type for the keyword field:



Figure 6


Click Next to review the selected options of the entity and then click Finish. JDeveloper will create the AdEntity in the ajaxejb package of the Application Sources folder:



Figure 7


Now you can add new fields and methods to the entity, using JDeveloper's wizards. Right-click Fields in the Structure navigator and click New Field. Provide the field's name, select its type and click OK:



Figure 8


After adding the url field, repeat the same procedure to add another field named content. Here is the source code of the AdEntity:

                                
package ajaxejb;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

@Entity
@NamedQuery(name = "AdEntity.findAll", 
            query = "select o from AdEntity o")
public class AdEntity implements Serializable {
    @Id
    private String keyword;
    public String url;
    public String content;

    public AdEntity() {
    }

    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

}
                       

In the following subsection, I'll show how to create a stateless session bean that uses AdEntity.

Creating a session bean

Right-click the ajaxejb project in the Applications navigator and click New. In the New Gallery window, expand the Business Tier node in the left panel and select EJB. Then, select Session Bean (EJB 1.1/2.x/3.0) in the right panel of the same window and click OK:



Figure 9


Skip the Welcome page of the Create Session Bean wizard, provide the AdSession name of the EJB, select the Stateless session type, select the Container transaction type and you can optionally instruct JDeveloper to Generate Session Facade Methods:



Figure 10


Next you can choose the methods to expose through the facade:



Figure 11


The third step lets you enter the name of the bean class. Enter ajaxejb.AdSessionBean and click Next:



Figure 12


The fourth step allows you to select the interfaces that the EJB will implement. We could use a local interface because we'll test the sample application in JDeveloper with a single OC4J instance. In a production environment, however, we might want to use a dedicated ad server (or maybe a cluster) running the session bean. The controller servlet that calls the bean's methods could be deployed on multiple Web servers and could also be used in different Web applications. By instructing JDeveloper to generate a Remote Interface named ajaxejb.AdSession, we'll still be able to run the sample application with the embedded OC4J server of JDeveloper, and we'll have maximum flexibility in a production environment. Nevertheless, a local interface would offer better performance if we were sure we want to deploy both the session bean and the controller servlet on the same server. It is also possible to implement both remote and local interfaces in the same bean. In this example, we'll implement only a remote interface:



Figure 13


Click Next to review the selected options of the session bean and then click Finish. JDeveloper will create the AdSessionBean in the ajaxejb package of the Application Sources folder:



Figure 14


Let's add now a business method named selectAd(), using the wizard offered by JDeveloper. Right-click Methods in the Structure navigator and click New Method. In the Bean Method Details dialog, enter the selectAd method name, select the ajaxejb.AdEntity return type, enter the String userInput parameter and click OK:



Figure 15


JDeveloper will update both the AdSession interface and the AdSessionBean class. Here is the source code of the AdSession interface:

package ajaxejb;

import java.util.List;

import javax.ejb.Remote;

@Remote
public interface AdSession {
    Object mergeEntity(Object entity);

    Object persistEntity(Object entity);

    List<adentity> queryAdEntityFindAll();

    void removeAdEntity(AdEntity adEntity);

    AdEntity selectAd(String userInput);
}

The AdSessionBean class generated by JDeveloper contains the session facade methods followed by the selectAd() method:

package ajaxejb;

import java.util.List;

import javax.ejb.Stateless;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless(name="AdSession")
public class AdSessionBean implements AdSession {
    @PersistenceContext(unitName="ajaxejb")
    private EntityManager em;

    public AdSessionBean() {
    }

    public Object mergeEntity(Object entity) {
        return em.merge(entity);
    }

    public Object persistEntity(Object entity) {
        em.persist(entity);
        return entity;
    }

    /**  
                               select o from AdEntity o */
    public List<adentity> queryAdEntityFindAll() {
        return em.createNamedQuery("AdEntity.findAll").getResultList();
    }

    public void removeAdEntity(AdEntity adEntity) {
        adEntity = em.find(AdEntity.class, adEntity.getKeyword());
        em.remove(adEntity);
    }

    public AdEntity selectAd(String userInput) {
        ...
    }
}

                            

Now we have to code the body of the selectAd() method.

Implementing the business method

A real-world application would have a Web interface for creating, updating and deleting AdEntity instances, using the methods of the AdSession bean. The selectAd() method would use some algorithm to match the user's interest to the ads retrieved from a database, using a query method. In this article, however, we'll keep things simple so that we can focus on the main topic. The selectAd() method will pick a random word from the user input and will return a new AdEntity instance:

package ajaxejb;
...
import java.util.Random;
import java.util.StringTokenizer;
...
public class AdSessionBean implements AdSession {
    ...
    public AdEntity selectAd(String userInput) {
        String keyword = "nothing";
        if (userInput != null && userInput.length() > 0) {
            StringTokenizer st = new StringTokenizer(
                userInput, ",.?!'& \t\n\r\f");
            int n = st.countTokens();
            if (n > 0) {
                int k = new Random().nextInt(n);
                for (int i = 0; i < k; i++)
                    st.nextToken();
                keyword = st.nextToken();
            }
        }
        AdEntity ad = new AdEntity();
        ad.setKeyword(keyword);
        ad.setUrl(keyword + ".com");
        ad.setContent("Buy " + keyword + " from " + ad.getUrl());
        return ad;
    }
}

Implementing the Ajax-EJB Controller

In this section, I'll create the controller servlet with JDeveloper. Then, I'll show how to use dependency injection, invoke the EJB's methods and generate the response for the Ajax client.

Creating the controller servlet

Right-click the ajaxejb project in the Applications navigator and click New. In the New Gallery window, expand the Web Tier node in the left panel and select Servlets. Then, select HTTP Servlet in the right panel of the same window and click OK:



Figure 16


Skip the Welcome page of the Create HTTP Servlet wizard and select Servlet 2.4\JSP 2.0 (J2EE 1.4):



Figure 17


Enter the AdServlet name of the servlet class, select the ajaxejb package for the servlet class, select the XML content type and click Next:



Figure 18


The next page of the wizard allows you to configure the servlet mapping:



Figure 19


The final page lets you enter information about the request parameters. In the case of this example, we'll use a single parameter named userInput:



Figure 20


Click Finish to generate the AdServlet class:



Figure 21


Now we have to modify the servlet. First of all, add the following lines that inject the AdSession bean, so that we can access it in the doGet() method of the servlet:

import javax.ejb.EJB;
...
public class AdServlet extends HttpServlet {

    @EJB(name="AdSession")
    private AdSession adSession;
    ...
}

In this example, we want the doGet() method to return an Ajax response that will contain information extracted from an AdEntity instance. The value of the userInput parameter is passed to the selectAd() method of the AdSession bean. The business method returns an instance of AdEntity that is stored into the request scope with setAttribute(). After that, doGet() sets the Content-Type and Cache-Control headers, and includes the content generated by a page named AdResponse.jsp. Here is the complete source of the AdServlet class:

package ajaxejb;

import javax.ejb.EJB;

import java.io.IOException;

import javax.servlet.*;
import javax.servlet.http.*;

public class AdServlet extends HttpServlet {

    @EJB(name="AdSession")
    private AdSession adSession;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response) 
                throws ServletException, IOException {
        String userInput = request.getParameter("userInput");
        AdEntity adEntity = adSession.selectAd(userInput);
        request.setAttribute("adEntity", adEntity);
        response.setHeader("Cache-Control", "no-cache");
        response.setContentType("text/xml");
        request.getRequestDispatcher("/AdResponse.jsp")
            .include(request, response);
    }
    
}

Creating the response page

Right-click the Web Content folder of the ajaxejb project in the Applications navigator and click New. In the New Gallery window, expand the Web Tier node in the left panel and select JSP. Then, select the JSP item in the right panel of the same window and click OK:



Figure 22


Skip the Welcome screen of the Create JSP wizard and enter the AdResponse.jsp name of the JSP page:



Figure 23


The next step of the Create JSP wizard lets you select an error handling option:



Figure 24


At the third step, you can select the JSP libraries that will be used in the page. For this example, select JSTL Core 1.1 in the left panel and move it into the right panel with the arrow button:



Figure 25


The fourth step allows you to provide other page options. Since the AdResponse.jsp page is used to generate an Ajax response, select None as the HTML version and leave an empty title:



Figure 26


Click the Finish button to generate the JSP page:



Figure 27


Remove the HTML content generated by JDeveloper and add the following lines that produce a link:

<a href='<c:out value="${requestScope.adEntity.url}"/>'>
    <c:out value="${requestScope.adEntity.content}"/>
</a>

The AdEntity instance used above is set as a request attribute in the AdServlet class.

Modifying the web.xml file

When you generated the AdServlet class, JDeveloper also created the web.xml application descriptor, which contains the servlet mapping information:

<web-app ...>
    ...
    <servlet>
        <servlet-name>AdServlet</servlet-name>
        <servlet-class>ajaxejb.AdServlet</servlet-class>
    </servlet>
    ...
    <servlet-mapping>
        <servlet-name>AdServlet</servlet-name>
        <url-pattern>/adservlet</url-pattern>
    </servlet-mapping>
    ...
</web-app>

You must change the Web application version from 2.4 to 2.5 and the schema file name from web-app_2_4.xsd to web-app_2_5.xsd so that you can use dependency injection in the AdServlet class:

    <web-app ...
        xsi:schemaLocation=".../web-app_2_5.xsd"
        version="2.5" ...>
      ...
    </web-app>

If you don't change the Web application version, the EJB will not be injected and the application will throw a NullPointerException.


Building the Ajax Client with JSF and ADF Faces

In this section, I'll create a JSF page and I'll use JDeveloper to add ADF Faces components. After that, I'll present the JavaScript code that invokes the controller servlet, passing the state of the ADF Faces components. The JSF page will be updated with the information retrieved from the Ajax response.

Creating the JSF page

Right-click the Web Content folder of the ajaxejb project in the Applications navigator and click New. In the New Gallery window, expand the Web Tier node in the left panel and select JSF. Then, select the JSF JSP item in the right panel of the same window and click OK:



Figure 28


Skip the Welcome screen of the Create JSF JSP wizard and enter the AdForm.jsp name of the JSF page:



Figure 29


The next step of the Create JSF JSP wizard lets you select a component binding option:



Figure 30


At the third step, you can select the JSP libraries that will be used in the JSF page. JSF Core and JSF HTML are selected by default. In this example, we'll also use the ADF Faces components. Therefore select ADF Faces Components and ADF Faces HTML in the left panel and move them into the right panel with the arrow button:



Figure 31


The fourth step allows you to provide other page options. Enter the Ajax-JSF Page title:



Figure 32


Click the Finish button to generate the JSF page:



Figure 33


Since this is the first JSF page of the application, JDeveloper creates the faces-config.xml file and configures the Faces Servlet in the web.xml file:

<web-app ...
    ...
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    ...
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    ...
</web-app>

Right click the h:form element of the AdForm.jsp page in the Structure navigator and click Convert. In the Convert Form dialog, select the ADF Faces Core library, select the Form element, and click OK:



Figure 34


JDeveloper will replace the <h:form> component with <af:form>. Since this is the first use of ADF Faces, JDeveloper automatically replaces the HTML tags of the page with <afh:html>, <afh:head> and <afh:body>. In addition, JDeveloper creates the adf-faces-config.xml file, and configures the adfFaces filter and the resources servlet in the web.xml file:

<web-app ...>
    <filter>
        <filter-name>adfFaces</filter-name>
        <filter-class>
            oracle.adf.view.faces.webapp.AdfFacesFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>adfFaces</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>
    ...
    <servlet>
        <servlet-name>resources</servlet-name>
        <servlet-class>
            oracle.adf.view.faces.webapp.ResourceServlet
        </servlet-class>
    </servlet>
    ...
    <servlet-mapping>
        <servlet-name>resources</servlet-name>
        <url-pattern>/adf/*</url-pattern>
    </servlet-mapping>
    ...
</web-app>

Go back to the AdForm.jsp page and right click the af:form element in the Structure navigator. Then, select Insert inside af:form and click JSF HTML:



Figure 35


Select Panel Grid in the Insert JSF HTML Item dialog and click OK:



Figure 36


Skip the Welcome screen of the Create PanelGrid wizard and enter 1 in the Number of Columns field:



Figure 37


Click Finish to insert the <h:panelGrid> component within the <af:form> element of the AdForm.jsp page. Right click the h:panelGrid element in the Structure navigator, select Insert inside h:panelGrid and click ADF Faces Core:



Figure 38


Select InputText in the Insert ADF Faces Core Item dialog and click OK:



Figure 39


JDeveloper inserts an <af:inputText> component within the <h:panelGrid> element of the AdForm.jsp page. Use the Property Inspector to change the label to User Input and the rows property to 4:



Figure 40


You can also use the Component Palette to insert components. In the text editor, move the caret cursor after the <af:inputText> element, select the ADF Faces Core library in the Component Palette and click Input Text to insert a second text field. Then, use the Property Inspector to change the label to More Input:



Figure 41


ADF Faces components support Partial Page Rendering (PPR) with the following attributes: autoSubmit, partialTriggers, and partialSubmit. You'll use these attributes if you want to update a component when the input value of another component (called trigger) is changed. If the autoSubmit attribute of an input component is true, the enclosing form is submitted when the component's value is changed in the browser. The components that must be updated use the partialTriggers attribute to specify the IDs of the trigger components.

When you use the attributes mentioned above, the ADF Faces components handle the communication and you don't have to worry about JavaScript event handlers and Ajax callbacks. There are cases, however, when you must use JavaScript and the XMLHttpRequest API in the browser. For example, if you want to submit the form data while the user is typing something or if you want to access resources such as our controller servlet, you'll need to provide your own JavaScript code.

The sample application contains a JavaScript file named AdScript.js. This file must be imported in the header of the page, using a <script> element within a <f:verbatim> component:

<f:verbatim>
  <script src="AdScript.js" type="text/javascript">
  </script>
</f:verbatim>

The AdScript.js file contains the JavaScript code for invoking the controller servlet. The Ajax response generated with AdResponse.jsp will be inserted within a <div> section of AdForm.jsp. The <div> element must be included in a <f:verbatim> component:

<f:verbatim>
  <div id="ad">
  </div>
</f:verbatim>

The form page needs some initialization after the browser finishes the loading. The initialization code can be found in the init() function of the AdScript.js file. Select the afh:body element of AdForm.jsp in the Structure navigator and set the onload property:



Figure 42


JDeveloper will modify the <afh:body> element:

<afh:body onload="init()">

Here is the complete source code of the AdForm.jsp page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ page contentType="text/html;charset=windows-1252"%>
<%@ taglib uri="http://xmlns.oracle.com/adf/faces" prefix="af"%>
<%@ taglib uri="http://xmlns.oracle.com/adf/faces/html" prefix="afh"%>
<f:view>
  <afh:html>
    <afh:head title="Ajax-JSF Page">
      <meta http-equiv="Content-Type"
            CONTENT="text/html;charset=utf-8"/>
      <f:verbatim>
        <script src="AdScript.js" type="text/javascript">
        </script>
      </f:verbatim>
    </afh:head>
    <afh:body onload="init()">
      <af:form>
        <h:panelGrid columns="1">
          <f:verbatim>
            <div id="ad">
            </div>
          </f:verbatim>
          <af:inputText label="User Input" rows="4"/>
          <af:inputText label="More Input"/>
        </h:panelGrid>
      </af:form>
    </afh:body>
  </afh:html>
</f:view>

The following subsection explains the code of the AdScript.js file.

Creating the JavaScript file

Right-click the Web Content folder of the ajaxejb project in the Applications navigator and click New. In the New Gallery window, expand the Web Tier node in the left panel and select HTML. Then, select the JavaScript File item in the right panel of the same window and click OK:



Figure 43


Enter the AdScript.js name of the JSF page and click OK:



Figure 44


JDeveloper will create an empty JavaScript file. Next you have to add the code that will invoke the Ajax-EJB controller. First of all, you need a function for creating the XMLHttpRequest object. The createRequest() function takes three parameters: the HTTP method, the URL of the Ajax controller, and the callback function. The XMLHttpRequest object is initialized with its open() method and then createRequest() sets the onreadystatechange property, which will contain a reference to the callback function that the XMLHttpRequest object will invoke every time its state is changed. Finally, createRequest() returns the object that will be used later to send the Ajax request:

function createRequest(method, url, callback) {
    var request;
    if (window.XMLHttpRequest)
        request = new XMLHttpRequest();
    else if (window.ActiveXObject)
        request = new ActiveXObject("Microsoft.XMLHTTP");
    else
        return null;

    request.open(method, url, true);

    function calbackWrapper() {
        callback(request);
    }
    request.onreadystatechange = calbackWrapper;

    return request;
}

The XMLHttpRequest object passes no parameter to the function that it calls when its state changes, but the callback will need the request object for obtaining the Ajax response. Therefore, createRequest() uses an inner function acting as a wrapper for the actual callback, which will obtain a reference to the request object.

Each XMLHttpRequest object should be used for a single Ajax request so that the application can work properly with all Ajax-capable browsers. In other words, you should not reuse the same XMLHttpRequest object for multiple requests. Nevertheless, these objects should be destroyed after they are used to avoid a memory leak in the browser. Form my experience, the safest way to destroy an XMLHttpRequest object is to set first its onreadystatechange property to a JavaScript function that does nothing, call the abort() method, and then free the memory with delete:

function deleteRequest(request) {
    function doNothing() {
    }
    request.onreadystatechange = doNothing;
    request.abort();
    delete request;
}

The adUpdate() function is the callback passed to createRequest(). If the request is completed (ready state is 4) and successful (status is 200), adUpdate() gets the content of the Ajax response from the responseText property of the request object. Then, adUpdate() stores the responseText into the innerHTML property of the element that has the ad id. This is the equivalent of inserting the Ajax response within the <div id="ad"></div> element of the AdForm.jsp page:

var debug = true;

function adUpdate(request) {
    if (request.readyState == 4) {
        if (request.status == 200) {
            var adSection = document.getElementById("ad");
            adSection.innerHTML = request.responseText;
        } else if (debug) {
            if (request.statusText)
                alert(request.statusText);
            else
                alert("HTTP Status: " + request.status);
        }
    }
}

In the sample application of this article, the user's input must be passed to the Ajax-EJB controller servlet. The getUserInput() function gets the values of all text fields and concatenates these values, returning a single string:

function getUserInput() {
    var userInput = "";
    var forms = document.forms;
    for (var i = 0; i < forms.length; i++) {
        var elems = forms[i].elements;
        for (j = 0; j < elems.length; j++) {
            var elemType = elems[j].type.toLowerCase();
            if (elemType == "text" || elemType == "textarea") {
                var elemValue = elems[j].value;
                userInput += " " + elemValue;
            }
        }
    }
    return userInput;
}

The sendAdRequest() function destroys the previous XMLHttpRequest object (if any), adds the userInput parameter to the URL, creates a new request object, and calls its send() method to submit the Ajax request to the controller servlet. The URL starts with .. because the servlet is invoked from a JSF page whose URL contains the /faces prefix:

var adURL = "../adservlet"
var adRequest = null;

function sendAdRequest() {
    if (adRequest)
        deleteRequest(adRequest);
    var url = adURL + "?userInput=" + escape(getUserInput());
    adRequest = createRequest("GET", url, adUpdate);
    adRequest.send(null);
}

The init() function calls sendAdRequest() to initialize the ad section of the page. Then, setInterval() is used to instruct the browser to call sendAdRequest() every second:

function init() {
    sendAdRequest();
    setInterval("sendAdRequest()", 1000);
}

Running the application

Right-click the AdForm.jsp page of the Web Content folder and click Run. JDeveloper will start the embedded OC4J server and will open the JSF page in a browser window. If you start typing something in one of the form's text fields, you'll notice how the ad changes every second.

Summary

In this article, you've learned how to build Java EE Ajax applications, using JDeveloper's visual editors and wizards for creating EJB3 components, Ajax-EJB controller servlets, and JSF pages based on ADF Faces. You've also learned several Ajax techniques, which will allow you to properly manage the XMLHttpRequest objects.


Andrei Cioroianu (devtools@devsphere.com) is the founder of Devsphere (www.devsphere.com), a provider of Java EE development and Ajax / JSF consulting services. Cioroianu has written many Java articles published by Oracle Technology Network, ONJava (www.onjava.com), JavaWorld (www.javaworld.com), and Java Developer’s Journal. He also coauthored the books Java XML Programmer’s Reference and Professional Java XML (both from Wrox Press).