Custom Integration of GWT Widgets into JSPs

11 01 2010

After spending certain time on wep-app development on GAE I came to the point where I wanted more AJAX and more interaction on my pages. Playing with pure “home-made” AJAX development clearly shown me that:

  • it is time consuming
  • it is not ok with cross-browsers compatibility

Logically my first though was to look into GWT. But this time I decided not to eagerly start learning all the GWT controls and there layouting. Instead I wanted to get high-level impression on strengths and weaknesses of this technology.

Soon I figured out its main weakness or let’s say inconvenience which was referenced by many and many blogs/posts online by different people. GWT plays very nice when it concerns browsers support BUT it generates a single host HTML page where all other essential for your business logic UI is built dynamically by javascript. This may be a good option if you want to build you web application from scratch but in real world we often need to mix different parts. Especially I was interested in how I can integrate GWT components, or widgets like they call it, into my already existing JSPs.

Let me describe an example. Suppose I want to have a component called NewsBlock. Suppose also I want to place it in my JSPs by a custom tag and pass a parameter to specify news category for example. So it should be something like:

<div>
<gwt-newsblock category='Politics'/>
</div>

Then I want this custom tag rendered to GWT implementation of the NewsBlock component.

Internet search offered only naive and primitive solutions which required hardcoding ids and presumed NO parametrisation. Namely they proposed to do the next thing:

In HTML:

<div id='containerID'>
</div>

In GWT EntryPoint.onModuleLoaded() method:

RootPanel.get('containerID').add(anyNeededWidget);

But you see here a hardcoding of the container ID in my GWT code which DOES NOT allow to isolate components from outer world (HTML/JSP).

Next research revealed me that GWT creators provided a very powerfull tool that may give me some more space for experiment: JSNI.

JSNI builds a bridge between your outer context javascript and the javascript that lives inside GWT. It also allows you to add in GWT Java code native functions (i.e. inject pure javascript code into not-yet-compiled Java).

The main idea is to expose part of the GWT module API to the outer javascript world. If I could have in my GWT Java code base a method like public void addNewsBlock(String category); that actually adds a widget and which is callable from my JSP-generated javascript then the problem would be solved.

API can be exported in the following way:

public class Refucktoring implements EntryPoint {

 private native void exportAPI()
 /*-{
 $wnd.addNewsBlock = @com.refucktoring.client.Refucktoring::addNewsBlock(Ljava/lang/String;);
 }-*/;

 public void onModuleLoad() {
 exportAPI();
 }

 private static void addNewsBlock(String containerId, String newsCategory)
 {
 RootPanel.get(containerId).add(new NewsBlock(newsCategory));
 }
}

Here 2 things are done:

  1. addNewsBlock(String, String) method is made visible to external javascript.
  2. containerId parameter is added to resolve IDs hardcoding problem. I will use this parameter at the point where my JSP custom tag will inject the GWT widget into some DIV element for example.

The nex step is integration in HTML. The very rude form to express it is:

<div id='containerId'>
<script type='text/javascript'>addNewsBlock('containerId', 'Politics');</script>
</div>

But there is a hidden obstacle: bootsrap sequence of the GWT module DOES NOT guarantee you that at the time when this HTML block evaluation will occur the GWT module is already loaded. So it may happen that when this short script will try to call addNewsBlock() method the onModuleLoaded() and its inner exportAPI() methos are not yet called and thus nothing will be added to your DIV.

So we need to add one more thing to allow GWT execute all this little outer javascrips itself after the module is loaded. Naturally would be good to handle this in the onModuleLoaded() method. Thus we add following to our EntryPoint class implementation:

public class Refucktoring implements EntryPoint {

 private native void exportAPI()
 /*-{
 $wnd.addNewsBlock = @com.refucktoring.client.Refucktoring::addNewsBlock(Ljava/lang/String;);
 }-*/;

 private native void executeExternalInjections()
 /*-{
 $wnd.executeInjections();
 }-*/;

 public void onModuleLoad() {
 exportAPI();
 executeExternalInjections();
 }

 private static void addNewsBlock(String containerId, String newsCategory)
 {
 RootPanel.get(containerId).add(new NewsBlock(newsCategory));
 }
}

Native method executeExternalInjections() is called automatically after exportAPI() to guarantee the proper sequence. That method calls external javascript function executeInjections() that should be added, as you already thought, by our JSP and is responsible for execution of all those widget-injections small scripts.

The last piece of the puzzle is actually implementation of that javascript method and nicely collecting this all together. Your JSP/HTML should add the following script to the HEAD of the document:

<script type="text/javascript">
 var injectorsHolder = new Object();
 function executeInjections()
 {
 for (var i in injectorsHolder)
 {
 injectorsHolder[i]();
 }
 }
 </script>
 <script type="text/javascript" language="javascript" src="refucktoring/refucktoring.nocache.js"></script>

As you see the sequence is important here: first preparatory script and only then GWT module loader script. Finally in places of injection (that can be generated by your custom JSP tags) you have this for example:

<div id="NewsContainer1">
 <script type="text/javascript">
 injectorsHolder['NewsContainer1'] = function() {addNewsBlock('NewsContainer1', 'Politics'');};
 </script>
 </div>

 <div id="NewsContainer2">
 <script type="text/javascript">
 injectorsHolder['NewsContainer2'] = function() {addNewsBlock('NewsContainer2', 'Sport'');};
 </script>
 </div>

The framework is ready and you can do now with GWT literally what you want :)
In the next post I will add an update to show the JSP custom tag implementation and show the overall solution working example.

——————————————————————————————————————————————————————

UPDATE

Here I give a simple example of the JSP custom tag since some of you asked for this in comments. This tag inserts one NewBlock gwt widget into the page.
So in the context described above our tag will look like this:

import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;

@SuppressWarnings("serial")
public class NewsBlockTag extends TagSupport {

 public void setId(String id)
 {
 this.id = id;
 }

 public int doStartTag()
 {
 try
 {
 pageContext.getResponse().setCharacterEncoding("UTF-8");
 JspWriter out = pageContext.getOut();
 out.println("<div id='" + id + "'>");
 out.println("<script type='text/javascript'>");
 out.println("injectorsHolder['NewsContainer1'] = function() {addNewsBlock('NewsContainer1', 'Politics'');};");
 out.println("</script>");
 out.println("</div>");
 }
 catch (Exception e)
 {
 e.printStackTrace();
 }
 return Tag.SKIP_BODY;
 }

 private String id;
}

So, the injection of 2 widgets which was arranged above as

<div id="NewsContainer1">
 <script type="text/javascript">
 injectorsHolder['NewsContainer1'] = function() {addNewsBlock('NewsContainer1', 'Politics'');};
 </script>
 </div>

 <div id="NewsContainer2">
 <script type="text/javascript">
 injectorsHolder['NewsContainer2'] = function() {addNewsBlock('NewsContainer2', 'Sport'');};
 </script>
 </div>

transforms into much compact form:

<gwt:newsBlock id="NewsContainer1"/>
<gwt:newsBlock id="NewsContainer2"/>

I omitted here other little things like your taglib descriptor, etc. But you can read it on the web, there are plenty of articles.





Supporting browser navigation and deep-linking in Flex applications

13 01 2009

In web-applications sometimes (if you try to do things good then always) it is needed to support browser navigation (back, forward buttons, bookmarks) and deep-linking.

This kind of requirements is not that hard to implement like it might seem. All you need to do in most of cases is to support switching to different states of your app by direct URLs. You just adds additional parameters to the URL pointing to the outer HTML page.

I’ve spent few hours yesterday to research in Flex documentation what weapons Flex provides for deep-linking. Everything appears to be very nice and elegant there.

Flex provides IBrowserManager interface and BrowserManager class which you use to handle deep-linking and communication with browser. Behind the scenes it employs History.css, History.js and some other files to work via JavaScript capabilities with different types of browsers.
Also URLUtil class will be handy. It’s equipped with methods allowing you quickly translate additional parameters you have in the URL to the object and vice verse to make it easier for you to work with those parameters.

I’ve tried to do some kind of a PoC and integrated this thing to one module of my current application and it perfectly worked with IE7 and FF3.  What we were able to see after just some 1.5 hours of work is:

- ability to go by URL directly to the details page while without deeplinking it was always needed to go first to the list page

- ability to use back and forward browser buttons. History was nicely supported.

- ability to bookmark the pages

Though, some people have impression that standard Flex framework do not nicely support deep-linking. This can have few possible explanations as I think:

  • some guys started to work with Flex quite long time ago when it was Flex 2. Those times as I understood people employed HistoryManager class to get history supported. And seems it didn’t worked properly with all browsers. Now Adobe says you need only use HistoryManager with Flex 2 and normally you must use BrowserManager.
  • If you run your application from the Flex Builder under IE it uses local file system URI and underlying java script works incorrectly in this case. In FF you have no troubles even with local file system URI. This problem has quite nice solution. Use as a compilation target folder for your SWFs some folder under Apache htdocs or some other simple web-server so you can access your app in IE browser via normal http URL. After this you will be able to test deeplinking features under IE.

That’s all. It proved to me that BrowserManager and related features are reliable and can be used.

If you need full overview of deep-linking in Adobe Flex implementation then go here: Deep Linking.








Follow

Get every new post delivered to your Inbox.