Integrating JSF, RichFaces and GWT (Part 2)
In the first part of the article, we showed how to let a JSF bean drive a GWT module through an exposed JavaScript API. Now we’ll see how to make a GWT module raise events in a way such that a call to the server is made letting it respond accordingly.
Defining the events to be raised
We’ll start by defining which events will be raised by our GWT module. Let’s put them all together in a single interface, which as a result of hours of thinking and thinking I decided to name IHelloWorldEventHandlers.
public interface IHelloWorldEventHandlers {
void requestHelloWorldAlert(String name);
}
Well, there will be only one event. Suppose we have a textbox containing the user name and a button which when clicked will raise the event.
Remember that one of our goals is to let JSF handle the request to the server, so the event will be implemented as a call to a JavaScript function which we’ll suppose to have already been defined somewhere. The concrete class which we’ll be called in the button’s onClick looks like the one below:
public class HelloWorldEventHandlers implements IHelloWorldEventHandlers {
public native void requestHelloWorldAlert(String name) /*-{
$wnd.requestHelloWorldAlert(name);
}-*/;
}
Some things to point out about this. You’ve probably have noticed that the method is defined with the “native” modifier. Think of this just as a way of wrapping a piece of JavaScript code with a Java method definition.
The second thing to point out is the $wnd reference. This is just a name for the global JavaScript scope in the browser.
The last, but the most important is that we are following a convention in the way we write the method body. The method in JavaScript is called exactly the same as the Java event handler, and it also has the same arity. This way we’ll be able to do some useful stuff by reflection later.
Dynamically Generating JavaScript handlers for the events
Now we have to create the JavaScript functions which will be called. The first idea that comes to mind is to write a <a4j:jsFunction> for each event. Well, that would work, but we can do better than that. There’s a common pattern between all the functions that we’ll need to write: we’ll want an action to be executed on a backing bean and we’ll have to create an <a4j:actionParam> for each parameter in the function.
We can then use reflection and the RichFaces and JSF object models to dynamically generate the functions for us. We decided to implement this through a new custom component. Again, this article does not intend to show how to write custom JSF components, but we’ll describe the basic concepts involved in the solution.
Our component we’ll need three attributes: the name of a backing bean, the name of a Map object property of the same bean and the name of an element to reRender.
We’ll override the setProperties method of our custom component CustomComponentTag class to add the dynamic generation of the jsFunction components through the following code:
FacesContext currentFacesCtx = FacesContext.getCurrentInstance();
Application application = currentFacesCtx.getApplication();
String reRenderedComponentId = (String) component.getAttributes()
.get("reRenderedComponent");
String beanId = (String) component.getAttributes().get(
"bean");
String functionParamsMapId = (String) component.getAttributes().get(
"functionParams");
for (Method method : IHelloWorldEventHandlers.class.getMethods()) {
HtmlAjaxFunction gwtEventHandler = new HtmlAjaxFunction();
//Set the name of the a4jFunction to be the same as the method
//name in IHelloWorldEventHandlers
gwtEventHandler.setName(method.getName());
gwtEventHandler.setReRender(reRenderedComponentId);
gwtEventHandler.setAction(application.createMethodBinding("#{"
+ beanId + "." + method.getName() + "}", null));
int i = 1;
for (Class> paramType : method.getParameterTypes()) {
String paramName = "Param" + i;
HtmlActionParameter htmlActionParam = new HtmlActionParameter();
htmlActionParam.setName(paramName);
htmlActionParam.setAssignToBinding(createValueBinding("#{"
+ beanId + "." + functionParamsMapName
+ "['" + method.getName() + "." + paramName + "']}"));
gwtEventHandler.getChildren().add(htmlActionParam);
htmlActionParam.setParent(gwtEventHandler);
//Important: we have to add this line in order to
//pass the parameter.
//It is not enough to add the actionParam as
//a child of the a4j:jsFunction.
gwtEventHandler.addActionListener(htmlActionParam);
i++;
}
component.getChildren().add(gwtEventHandler);
}
Note that we only need to know the IHelloWorldEventHandlers to generate the a4jFunction components. If we refactor the handlers changing their arity or name, this stuff will go on working. The only caveat is that we are forced to write those weird native methods in the implementation of IHelloWorldEventhandlers, but we think it is possible to work around that problem using GWT Generators also.
Another thing to point out is that we are using the Map to pass parameters, where the method name concatenated with a generated param name is used as key.
Martín
Integrating JSF, RichFaces and GWT (Part 1)
Since some time ago, there seems to be a fight between two “philosophies” as regards web applications. Each philosophy has its own representatives amongst the various frameworks or platforms we can select to help us develop our app. On one hand there are the page-oriented web applications, some of whose representatives are ASP.NET and JSP (and JSF, Struts, etc.). On the other hand there are component-oriented web applications supported by technologies such as GWT, Flash, Silverlight and so on.
We know there are some requirements which are better suited by page-oriented (for example, the classic shopping cart solution) apps, whereas some others are ideal for a component-oriented approach (GMail being the most remarkable example). However, there are scenarios where both kind of requirements coexist for the same web application. So, why would we be forced to choose only one approach and discard the other?
In this article, we’ll try to show how we managed to integrate a GWT component into the JSF page-oriented life cycle with the addition of JBoss RichFaces to support Ajax calls (and partial HTML rerenders), and trying to minimize the number of hardcoded JavaScript lines.
Before going into deeper implementation details, let’s see what problems we had to work out.
First, we needed to communicate with the GWT component, so it had to expose a JavaScript API. Furthermore, we didn’t want to stand on a JavaScript API, which would have implied having to write non-refactorable (does that word even exist?) JavaScript code. We wanted the GWT component to be driven by a JSF bean through the API, based on a server side model. So in fact, the JavaScript client code would be generated at the server side rather than hardcoded into a HTML or JS file. That’s why it seemed nice to have the JavaScript API’s client code generated somehow by Java code, so that it was able to automatically adapt to changes in the JavaScript API methods’ signature.
Second, we needed to implement some mechanism to let GWT raise events, and the JSF page respond to them. The constraint of having as little hardcoded JavaScript as possible is also present here.
Let’s see how we addressed the first problem in this part of the article, and leave the second for the next part.
We assume basic knowledge from the reader about Java, JavaScript, JSP, JSF, RichFaces, Ajax and GWT.
Exposing a JavaScript API from a GWT module
Well, we want to be able to call our GWT component from the outside, so, since we are in the “browser world”, the only way to do it is through an API which the component itself exposes. The problem is that the GWT Java-to-JavaScript compiler performs obfuscation. This particular issue was really easy to solve, since we didn’t do it
. There’s a very nice library called gwt-exporter which did the work for us.
Say we have the HelloWorld GWT example and we want to expose a JavaScript API method “helloWorld()” to invoke the alert message “Hello world” from outside the GWT module.
With gwt-exporter, we only have to write a class which looks as the one below:
/* @gwt.export */
public class HelloWorldAPI implements Exportable {
public void helloWorld(){
Window.alert("Hello world!");
}
}
If we want to call the helloWorld from JavaScript, we just have to execute the following code:
var helloWorldAPI = new HelloWorldAPI(); helloWorldAPI.helloWorld();
Generating the JavaScript code from server-side Java code
Thanks to gwt-exporter, we’re half way there. But we still have to hardcode JavaScript code.
Suppose we’re using Eclipse, we know how easy it is to say “Uhm, I don’t like how ‘helloWorld’ sounds, I’ll refactor it to ‘goodMorningWorld’”. Eclipse performs the refactoring in less than a second within the Java code, but the hardcoded JavaScript remains untouched, thus breaking the calls to the GWT component.
Typically, the client code to call the API will look always the same: create an instance of the API class (if we still haven’t created one) and call the API method we want to execute. So, since the JavaScript API method signature is generated from the Java class method signature, we could reflect in some way over the latter to generate that typical piece of code. This way, when we refactor a method in HelloWorldAPI, our generated code we’ll adapt to the new method name. We can achieve this with the help of Java’s Proxy class (java.lang.reflect.Proxy), following the steps below:
1) Extract the API methods into an interface:
public interface IHelloWorldAPI {
void helloWorld();
}
2) Make the original API class implement the new interface:
public class HelloWorldAPI implements Exportable, IHelloWorldAPI {
public void helloWorld(){
Window.alert("Hello world!")
}
}
3) In the bean’s code, create a proxy instance of the new interface:
InvocationHandler helloWorldAPIInvocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
StringWriter stringWriter = new StringWriter();
stringWriter.append("if (helloWorldAPI == null)
var helloWorldAPI = new HelloWorldAPI(); helloWorldAPI.");
stringWriter.append(method.getName());
stringWriter.append("(");
for (int i = 0; i < method.getParameterTypes().length; i++) {
if (method.getParameterTypes()[i].equals(String.class)){
stringWriter.append("'" + args[i] + "'");
} else {
stringWriter.append(args[i].toString());
}
if (i != method.getParameterTypes().length - 1)
stringWriter.append(", ");
}
stringWriter.append(")");
stringWriter.append(";");
queue.add(stringWriter.toString());
return null;
}
};
Class helloWorldAPIProxy =
(Class) Proxy.getProxyClass(
IHelloWorldAPI.class.getClassLoader(),
new Class[] { IHelloWOrldAPI.class });
IHelloWorldAPI helloWorldAPI =
(IHelloWorldAPI) helloWorldAPIProxy.getConstructor(
new Class[] {InvocationHandler.class})
.newInstance(new Object[] {helloWorldAPIInvocationHandler });
Then, to call the JS API, we only have to use the helloWorldAPI instance and call the method we want to.
If you pay attention to the code above, you’ll see that the invocation handler is trapping all the calls to the helloWorldAPI object and generating the typical JS code we were talking about, getting the method name and param types by reflection. Then, it queues the generated JavaScript. We didn’t say anything about that queue.
Remember that this code is server-side and the GWT component is ultimately a piece of JavaScript rendered in the browser, so we need some way of getting the newly generated JavaScript code with the calls to the GWT module API from the server to the client browser.
To do this, we wrote a custom RichFaces component which is binded to a queue of String, and when rendered gets the queue and iterates over its elements to write them into a
tag. This code is executed on the components reRender event. So, if we want a RichFaces action to let the server make calls to the GWT component, we have to add to its reRender attribute the custom component\'s id.
We won\'t discuss the implementation details of this custom component, since we think it\'s not important to show the integration we are trying to achieve, and anyone with some experience using JSF and RichFaces could write something similar with ease.
I hope the article was clear enough to let you understand the basic ideas behind the solution.
In part 2 we\'ll show how we implemented a way of letting GWT raise events and JSF-RichFaces respond to them.
Martín