Level: Advanced Sergey Odobetskiy (s.odobetsky@gmail.com), Software development consultant , Freelance
24 Apr 2007 The Google Web Toolkit (GWT) provides libraries and tools that let you develop Ajax applications in the Java™ programming language. Unfortunately, GWT's standard gallery of UI controls (widgets) doesn't provide the advanced features that modern enterprise applications require. This article shows a technique that addresses this deficiency. Find out how to give GWT controls advanced functionality with relatively simple coding by integrating a popular JavaScript grid component with a GWT application.
Open source Ajax frameworks and commercial toolkits try to make Ajax development more accessible by hiding complexity and the burden of low-level JavaScript and Document Object Model (DOM) operations. They often help to reduce development time, but using them to achieve advanced functionality usually requires a skill set and level of JavaScript knowledge that aren't trivial to acquire. Google has come up with innovative technology that brings Ajax application development to a completely new level. The GWT is a comprehensive set of APIs and tools that lets you create dynamic Web applications in Java code. GWT does this through code generation: The GWT compiler generates cross-browser compatible JavaScript from client-side Java code.
Unfortunately, the standard UI controls (widgets) Google offers have a limited feature set and in many cases cannot satisfy modern enterprise-grade applications. Although you can extend basic GWT controls with a set of custom properties, this path often requires significant development effort. Fortunately, an easier approach is available. With the help of a sample application (see Download), this article shows how to integrate the popular ActiveWidgets JavaScript grid component with a GWT application and achieve advanced functionality with relatively simple coding.
Dynamic Web UI controls
JavaScript Web UI controls are designed to eliminate server roundtrips by generating HTML dynamically inside the browser. Instead of sending static HTML, the server adds data structures and JavaScript code for the dynamic components into the page content. During page load, the browser runs the scripts to create active components, configures them, and then inserts the HTML string each component generates into the proper place on the page. From this point, the JavaScript component code is connected to the HTML fragment on the page. The component manages user interactions, updates the HTML according to the data changes, and provides an API to manipulate its content, behavior, and visual style.
GWT controls developed in Java code and compiled into JavaScript work exactly the same way. This makes them fully compatible with commercial controls and JavaScript libraries. JavaScript libraries are more mature and provide a complete set of cross-browser advanced features such as intelligent scrolling, column resizing, sorting, dynamic loading, and paging for large data sets. GWT's open architecture lets you integrate commercial controls and legacy JavaScript libraries with new applications and focus on solving enterprise challenges rather then reinventing the proverbial wheel.
Automatic resource injection
This article's sample GWT application (see Download) uses a handy GWT feature: automatic resource injection. A project module includes references to external JavaScript and Cascading Style Sheets (CSS) files, causing them to load automatically when the module itself is loaded. Listing 1 shows the declarations in the sample application's module-definition XML file (GridDemo.gwt.xml) that achieve this:
Listing 1. GWT module definition
<module>
<inherits name='com.google.gwt.user.User'/>
<entry-point class='com.mycompany.project.client.GridDemo'/>
<source path="client"/>
<stylesheet src="runtime/styles/xp/aw.css"/>
<script src="runtime/lib/aw.js">
<![CDATA[
if ($wnd.AW.Grid.Control.create)
return true;
else
return false;
]]>
</script>
</module>
|
Injection of JavaScript files and stylesheets is a convenient way to associate external files automatically with your GWT module. All injected resources must be placed in the com\mycompany\project\public directory, which defines the GWT Web server root.
AW.Grid.Control.create is a script ready-function that returns true when the grid-control script is known to be initialized. The ready-function's purpose is to determine unambiguously that the script is fully loaded so that your GWT code can use it and be sure the referenced identifiers are available. In the example in Listing 1, the existence of the AW.Grid.Control.create function indicates that the script is ready. This function is part of the control's library and as an external resource, it must be prefixed with $wnd.
JavaScript Native Interface to the rescue
Integration of the JavaScript grid control itself is based on GWT's implementation of the JavaScript Native Interface (JSNI), a powerful feature that lets you include JavaScript code in Java source code. Listing 2 shows how the JavaScript grid control is instantiated and initialized via JSNI:
Listing 2. Grid control initialization (fragment)
native JavaScriptObject init(JavaScriptObject myColumns,JavaScriptObject myData)/*-{
try{
$wnd.mygrid = new $wnd.AW.UI.Grid;
$wnd.mygrid.setSize(750, 350);
// provide cells and headers text
$wnd.mygrid.setCellText(myData);
$wnd.mygrid.setHeaderText(myColumns);
// set number of rows/columns
$wnd.mygrid.setRowCount(myData.length);
$wnd.mygrid.setColumnCount(myColumns.length);
...
$doc.getElementById('mygrid').innerHTML = $wnd.mygrid;
return $wnd.mygrid;
}
catch(e){
$wnd.alert(e.description);
}
return null;
}-*/;
|
JSNI can be considered the Web equivalent of inline assembly code. You can use it to:
- Implement a Java method directly in JavaScript.
- Wrap type-safe Java method signatures around existing JavaScript.
- Call from JavaScript into Java code and vice versa.
- Throw exceptions across Java/JavaScript boundaries.
- Read and write Java fields from JavaScript.
- Use hosted mode to debug both Java source (with a Java debugger) and JavaScript (with a script debugger).
The GWT development documentation warns that JSNI must be used with discretion because browser portability isn't guaranteed, and the compiled code optimization is restrictive. In the case of commercial controls, you can rely on the vendors' commitment to supporting multiple browser types and deployment platforms.
When you access the browser's window and document objects from JSNI, you must reference them as $wnd and $doc, respectively. Your compiled script runs in a nested frame, and $wnd and $doc are automatically initialized to refer correctly to the host page's window and document.
The sample code demonstrates a useful technique, using JSNI for converting one- and two-dimensional Java string arrays into JavaScript arrays to populate a grid with data, as shown in Listing 3:
Listing 3. Java-to-JavaScript array transformation
public static JavaScriptObject arrayConvert(String[] array) {
JavaScriptObject result = newJSArray(array.length);
for (int i = 0; i<array.length; i++) {
arraySet(result, i, array[i]);
}
return result;
}
private static native JavaScriptObject newJSArray(int length) /*-{
return new Array(length);
}-*/;
public static native int arrayLength(JavaScriptObject array) /*-{
return array.length;
}-*/;
public static native String arrayGetObject(JavaScriptObject array, int index) /*-{
return array[index];
}-*/;
public static native void arraySet(JavaScriptObject array,int index,String value) /*-{
array[index] = value;
}-*/;
|
Processing events
Event handlers defined in the JavaScript can be associated with methods implemented in your Java code. Listing 4 demonstrates how the grid control's onRowClicked event invokes the onRowSelect Java function:
Listing 4. Java function invocation from JavaScript
public void onRowSelect(String index){
GWT.log("Row #" + index + "selected", null);
}
native JavaScriptObject init(JavaScriptObject myColumns, JavaScriptObject myData)/*-{
var widget = this;
try{
...
// set click action handler
$wnd.mygrid.onRowClicked = function(event, index){
widget.@com.mycompany.project.client.GwtGrid::onRowSelect(Ljava/lang/String;)(index);
};
...
}
catch(e){
$wnd.alert(e.description);
}
}-*/;
|
You must reference Java methods in JavaScript using the following notation:
instance-expr.@class-name::method-name(param-signature)(arguments)
-
instance-expr.
must be present when you call an instance method and must be absent when you call a static method.
-
class-name
is the fully qualified name of the class in which the method is declared (or a subclass thereof).
-
param-signature
is the internal Java method signature as specified here but without the trailing signature of the method return type (because it isn't needed to choose the overload).
-
arguments
is the actual argument list to pass to the called method.
Using the JavaScript control API
As you can see from Listing 2, the init method of the implementation's Java class returns JavaScriptObject, which represents a grid instance. You can store it as a class attribute and reference it later in the code to invoke JavaScript control API methods. Listing 5 shows how a Delete button click event is processed in Java code:
Listing 5. JavaScript method invocation
protected JavaScriptObject grid = null;
...
public void onLoad(){
if(grid == null){
grid = init(...);
}
}
public void onDeleteButtonClick(){
delete(grid, getCurrentRow(grid));
}
public native void delete(JavaScriptObject obj, int index) /*-{
try{
obj.deleteRow(index);
}
catch(e){
$wnd.alert(e.description);
}
}-*/;
public native int getCurrentRow(JavaScriptObject obj) /*-{
try{
return obj.getCurrentRow();
}
catch(e){
$wnd.alert(e.description);
}
return -1;
}-*/;
|
In Listing 5, a Java event handler calls JavaScript control API methods. This demonstrates the ability to interact with the control instance. You can define similar Java wrappers for all JavaScript control API methods and make them available to the GWT world.
Runtime configuration
The demo application requires that you install the GWT SDK on your system. To run the sample code, simply unzip the downloaded archive with sources and compiled scripts into the GWT SDK distribution directory's samples folder. If you can run GWT samples, you can launch the sample application with following command (on Windows platforms):
your_local_path\gwt-windows-1.3.3\samples\GridDemo\GridDemo-shell.cmd
|
The sample GWT application demonstrates integration of the commercial JavaScript grid component developed by ActiveWidgets and offered for evaluation download at company's Web site (see Resources). Figure 1 shows the result:
Figure 1. Advanced grid control in sample GWT application
The demo application archive contains all required libraries except GWT. I've tested it with all major GWT releases, including version 1.3.
Conclusion
This article presents an example of how to integrate legacy JavaScript grid controls with GWT applications. You can use the same approach for other advanced controls available on the market. As an alternative to commercial JavaScript controls, check out the Yahoo! User Interface Library, an open source set of JavaScript utilities and controls for building richly interactive Web applications using techniques such as DOM scripting, DHTML, and Ajax (see Resources).
Download | Description | Name | Size | Download method |
|---|
| Sample GWT application | j-gwtcontrols.zip | 503KB | HTTP |
|---|
Resources Learn
- "What Is Web 2.0" (Tim O'Reilly, O'Reilly Network, September 2005): Design patterns and business models for the next generation of software.
- "Ajax for Java developers: Exploring the Google Web Toolkit" (Philip McCarthy, developerWorks, June 2006): Get a comprehensive, hands-on introduction to GWT.
-
Ajax resource center: One-stop shop for information on the Ajax programming model, including articles and tutorials, discussion forums, blogs, wikis, events, and news.
-
The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
Discuss
About the author  | |  | Sergey Odobetskiy is a software development consultant specializing in Java and Web technologies. He has recently been involved in several telecom projects at Rogers Communications and is currently working in Toronto with ConceptWave Software Inc. on a telecom order-care product. |
Rate this page
|