Seam and AJAX

0
103
10 min read

What is AJAX?

AJAX (Asynchronous JavaScript and XML) is a technique rather than a new technology for developing highly interactive web applications. Traditionally, when JavaScript is written, it uses the browser’s XMLHttp DOM API class to make asynchronous calls to a server-side component, for example, servlets. The server-side component generates a resulting XML package and returns this to the client browser, which can then update the browser page without having to re-render the entire page. The result of using AJAX technologies (many different technologies can be used to develop AJAX functionality, for example, PHP, Microsoft .NET, Servlets, and Seam) is to provide an appearance similar to a desktop, for web applications.

AJAX and the Seam Framework

The Seam Framework provides built-in support for AJAX via its direct integration with libraries such as RichFaces and AJAX4JSF. Discussing the AJAX support of RichFaces and AJAX4JSF could fill an entire book, if not two books, so we’ll discuss these technologies briefly, towards the end of this article, where we’ll give an overview of how they can be used in a Seam application.

However, Seam provides a separate technology called Seam Remoting that we’ll discuss in detail in this article. Seam Remoting allows a method on Seam components to be executed directly from JavaScript code running within a browser, allowing us to easily build AJAX-style applications.

Seam Remoting uses annotations and is conversation-aware, so that we still get all of the benefits of writing conversationally-aware components, except that we can now access them via JavaScript as well as through other view technologies, such as Facelets.

Seam Remoting provides a ready-to-use framework, making AJAX applications easier to develop. For example, it provides debugging facilities and logging facilities similar to the ones that we use everyday when writing Java components.

Configuring Seam applications for Seam Remoting

To use Seam Remoting, we need to configure the Seam web application to support JavaScript code that is making asynchronous calls to the server back end. In a traditional servlet-based system this would require writing complex servlets that could read, parse, and return XML as part of an HTTP GET or POST request. With Seam Remoting, we don’t need to worry about managing XML data and its transport mechanism. We don’t even need to worry about writing servlets that can handle the communication for us—all of this is a part of the framework.

To configure a web application to use Seam Remoting, all we need to do is declare the Seam Resource servlet within our application’s WEB-INF/web.xml file. We do this as follows.

<servlet>
<servlet-name>Seam Resource Servlet</servlet-name>
<servlet-class>
org.jboss.seam.servlet.SeamResourceServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Seam Resource Servlet</servlet-name>
<url-pattern>/seam/resource/*</url-pattern>
</servlet-mapping>

That’s all we need to do to make a Seam web application work with Seam Remoting. To make things even easier, this configuration is automatically done when applications are created with SeamGen, so you would have to worry about this configuration only if you are using non-SeamGen created projects.

Configuring Seam Remoting server side

To declare that a Seam component can be used via Seam Remoting, the methods that are to be exposed need to be annotated with the @WebRemote annotation. For simple POJO components, this annotation is applied directly on the POJO itself, as shown in the following code snippet.

@Name("helloWorld")
public class HelloWorld implements HelloWorldAction {

@WebRemote
public String sayHello() {
return "Hello world !!";
}

For Session Beans, the annotation must be applied on the Session Beans business interface rather than on the implementation class itself. A Session Bean interface would be declared as follows.

import javax.ejb.Local;
import org.jboss.seam.annotations.remoting.WebRemote;

@Local
public interface HelloWorldAction {
@WebRemote
public String sayHello();
@WebRemote
public String sayHelloWithArgs(String name);
}

The implementation class is defined as follows:

import javax.ejb.Stateless;
import org.jboss.seam.annotations.Name;

@Stateless
@Name("helloWorld")
public class HelloWorld implements HelloWorldAction {
public String sayHello() {
return "Hello world !!";
}
public String sayHelloWithArgs(String name) {
return "Hello "+name;
}
}

Note that, to make a method available to Seam Remoting, all we need to do is to annotate the method with @WebRemote and then import the relevant class. As we can see in the preceding code, it doesn’t matter how many parameters our methods take.

Configuring Seam Remoting client side

In the previous sections, we’ve seen that minimal configuration is required to enable Seam Remoting and to declare Seam components as Remoting-aware. Similarly in this section, we’ll see that minimal work is required within a Facelets file to enable Remoting.

The Seam Framework provides built-in JavaScript to enable Seam Remoting. To use this JavaScript, we first need to define it within a Facelets file in the following way:

<script type="text/javascript" src="/HelloWorld/seam/resource/
remoting/resource/remote.js">
</script>
<script type="text/javascript" src="/HelloWorld/seam/resource/
remoting/interface.js?helloWorld">

To include the relevant JavaScript into a Facelets page, we need to import the /seam/resource/remoting/resource/remote.js and /seam/resource/remoting/interface.js JavaScript files. These files are served via the Seam resource servlet that we defined earlier in this article.

You can see that the interface.js file takes an argument defining the name of the Seam component that we will be accessing (this is the name of the component for which we have defined methods with the @WebRemote annotation).

If we wish to use two or more different Seam components from a Remoting interface, we would specify their names as parameters to the interface.js file separated by using an “&“, for example:

<script type="text/javascript" src="/HelloWorld/seam/resource/
remoting/interface.js?helloWorld&mySecondComponent&
myThirdComponent">

To specify that we will use Seam components from the web tier is straight-forward, however, the Seam tag library makes this even easier.

Instead of specifying the JavaScript shown in the preceding examples, we can simply insert the <s:remote /> tag into Facelets, passing the name of the Seam component to use within the include parameter.

<ui:composition






template="layout/template.xhtml">

<ui:define name="body">
<h1>Hello World</h1>
<s:remote include="helloWorld"/>

To use the <s:remote /> tag, we need to import the Seam tag library, as shown in this example. When the web page is rendered, Seam will automatically generate the relevant JavaScript.

Seam and AJAX

If we are using the <s:remote /> tag and we want to invoke methods on multiple Seam components, we need to place the component names as comma-separated values within the include parameter of the tag instead, for example:
<s:remote include=”helloWorld, mySecondComponent, myThirdComponent” />

Invoking Seam components via Remoting

Now that we have configured our web application, defined the services to be exposed from the server, and imported the JavaScript to perform the AJAX calls, we can execute our remote methods.

To get an instance of a Seam component within JavaScript, we use the Seam.Component.getInstance() method. This method takes one parameter, which specifies the name of the Seam component that we wish to interact with.

Seam.Component.getInstance("helloWorld")

This method returns a reference to Seam Remoting JavaScript to allow our exposed @WebReference methods to be invoked. When invoking a method via JavaScript, we must specify any arguments to the method (possibly there will be none) and a callback function. The callback function will be invoked asynchronously when the server component’s method has finished executing. Within the callback function we can perform any JavaScript processing (such as DOM processing) to give our required AJAX-style functionality.

For example, to execute a simple Hello World client, passing no parameters to the server, we could define the following code within a Facelets file.

<ui:define name="body">
<h1>Hello World</h1>
<s:remote include="helloWorld"/>
<p>
<button onclick="javascript:sayHello()">Say Hello</button>
</p>
<p>
<div id="helloResult"></div>
</p>
<script type="text/javascript">
function sayHello() {
var callback = function(result) {
document.getElementById("helloResult").innerHTML=result;
};
Seam.Component.getInstance("helloWorld").
sayHello(callback);
}
</script>
</ui:define>

Let’s take a look at this code, one piece at a time, to see exactly what is happening.

<s:remote include="helloWorld"/>

<p>
<button onclick="javascript:sayHello()">Say Hello</button>
</p>

In this part of the code, we have specified that we want to invoke methods on the helloWorld Seam component by using the <s:remote /> tag. We’ve then declared a button and specified that the sayHello() JavaScript method will be invoked when the button is clicked.

<div id="helloResult"></div>

Next we’ve defined an empty <div /> called helloResult. This <div /> will be populated via the JavaScript DOM API with the results from out server side method invocation.

<script type="text/javascript">
function sayHello() {
var callback = function(result) {
document.getElementById("helloResult").innerHTML=result;
};
Seam.Component.getInstance("helloWorld").
sayHello(callback);
}
</script>

Next, we’ve defined our JavaScript function sayHello(), which is invoked when the button is clicked. This method declares a callback function that takes one parameter. The JavaScript DOM API uses this parameter to set the contents of the helloResult <div /> that we have defined earlier.

So far, everything that we’ve done here has been simple JavaScript and hasn’t used any Seam APIs. Finally, we invoke the Seam component using the Seam.Component.getInstance().sayHello() method, passing the callback function as the final parameter.

When we open the page, the following flow of events occurs:

  1. The page is displayed with appropriate JavaScript created via the<s:remote /> tag.
  2. The user clicks on the button.
  3. The Seam JavaScript is invoked, which causes the sayHello() method on the helloWorld component to be invoked.
  4. The server side component completes execution, causing the JavaScript callback function to be invoked.
  5. The JavaScript DOM API uses the results from the server method to change the contents of the <div /> in the browser, without causing the entire page to be refreshed.

This process shows how we’ve developed some AJAX functionality by writing a minimal amount of JavaScript, but more importantly, by not dealing with XML or the JavaScript XMLHttp class.

The preceding code shows how we can easily invoke server side methods without passing any parameters. This code can easily be expanded to pass parameters, as shown in the following code snippet:

<s:remote include="helloWorld"/>
<p>
<button onclick="javascript:sayHelloWithArgs()">
Say Hello with Args
</button>
</p>
<p>
<div id="helloResult"></div>
</p>
<script type="text/javascript">
function sayHelloWithArgs() {
var name = "David";
var callback = function(result) {
document.getElementById("helloResult").innerHTML=result;
};
Seam.Component.getInstance("helloWorld").
sayHelloWithArgs(name, callback);
}
</script>

The preceding code shows that the process for invoking remote methods with parameters is similar to the process for invoking remote methods with no parameters. The important point to note is that the callback function is specified as the last parameter.

When our simple application is run, we get the following screenshot.

Seam and AJAX

Clicking on either of the buttons on the page causes our AJAX code to be executed, and the text of the <div /> component to be changed.

Seam and AJAX

If we want to invoke a server side method via Seam Remoting and we want the method to be invoked as a part of a Seam conversation, we can use the Seam.Remoting.getcontext.setConversationId() method to set the conversation ID. This ID will then by used by the Seam Framework to ensure that the AJAX request is a part of the appropriate conversation. Seam.Remoting.getContext().setConversationId(#{conversation.id});

LEAVE A REPLY

Please enter your comment!
Please enter your name here