The Google Web Toolkit (GWT) allows you to write an application interface in Java and then compile it to script. That capability makes it easy to envision the interface working more like a standalone application, communicating with the server directly using the SAND messages. It also dispenses with a lot of XHTML state information, increases communications efficiency, and generally creates a much better impedence match between front and back end development.
Of course there are drawbacks also. Script isn't as well supported as a plain HTML display, and script compilation doesn't provide full JRE emulation. But the advantages can be huge, especially for displays where data is updated asynchronously. At the time of this writing, the best approach seems to be a mix of traditional HTML and AJAX displays.
Since GWT development occurs within its own development project environment and is not part of the core prerequisite technology for SAND, you will need to modify your build to integrate things. The good news is that we've provided a lot of support to make this as easy as possible. To demonstrate, here's how to create a very simple stats display interface using GWT that will connect to the TaskHeap demo project:
If you haven't done so already, download the GWT for your platform. Unpack it somewhere outside of your source tree.
Run the applicationCreator script to create the gwt project. GWT is a separate project, but I found it easiest to put it in a gwt directory off the TaskHeapDemo deployment area. Here's what I did:
cd /general/gwt-mac-1.4.62/
./applicationCreator -out /general/sand/deploy/TaskHeapDemo/gwt org.TaskHeap.client.THMonitor
./projectCreator -ant THMonitor -out /general/sand/deploy/TaskHeapDemo/gwt
This creates the core files you will need to create the UI, and a
build file to compile everything you need to run a local server for
testing. You will also want to give the GWT compiler enough room to
work with, so edit the THMonitor-compile
and THMonitor-shell scripts to
specify -Xmx256M in the java call.
If you want, you can edit
gwt/src/org/TaskHeap/public/THMonitor.html to change the
text. The important points to keep are the script inclusion and the
"slot1" and "slot2" areas we will be writing
to.
Edit gwt/src/org/TaskHeap/client/THMonitor.java to
call the server and display some stats. First, we will need to add
the following imports:
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.sandev.basics.sandmessages.Stats;
import org.sandev.basics.structs.SandAttrVal;
Then we need to edit onModuleLoad to make the server
call and display the results. Here's what it looked like after
editing:
public void onModuleLoad() {
final Button button = new Button("reset");
final Label label = new Label("click: 0");
final SANDComms thsc=new SANDComms();
final AsyncCallback callback=new AsyncCallback()
{
public void onSuccess(Object result)
{
Stats stats=(Stats)result;
SandAttrVal[] savs=stats.getData();
String text=result.toString() + savs.length;
for(int i=0;i<savs.length;i++) {
text+=", " + savs[i].getAttr() + ": " + savs[i].getVal(); }
label.setText(text);
thsc.call(stats,this);
}
public void onFailure(Throwable e)
{
label.setText("error calling: " + e);
}
};
button.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
thsc.call(new Stats(),callback);
}
});
// Assume that the host HTML has elements defined whose
// IDs are "slot1", "slot2". In a real app, you probably would not want
// to hard-code IDs. Instead, you could, for example, search for all
// elements with a particular CSS class and replace them with widgets.
//
RootPanel.get("slot1").add(button);
RootPanel.get("slot2").add(label);
button.click();
}
So the button got changed to be called "reset", and it gets clicked
automatically when the module loads. Clicking it makes a call back to
the TaskHeap server comms. This is an async call that gets returned
when it completes.
For THMonitor to compile, we need to provide the Stats message and
the SANDComms class. To make this happen edit build.xml
in the TaskHeapDemo deployment build directory and modify
the "sandproject" target. After the
existing <sandui> definition add the
following:
<!-- GWT interface support -->
<sandui
name="gwt/sandgwt" type="GWT"
implClass="org.TaskHeap.server.SANDMessageServiceImpl"/>
<sandenvfile name="gwt-servlet.jar"/>
<sandgenerator classname="org.sandev.generator.CommExportGenerator"
source="structs" sourcescope="build" runscope="project"
extra="SANDGWT,../gwt/src/org/TaskHeap/client,TaskHeapUINodeDecl"/>
Then copy over gwt-servlet.jar into the TaskHeapDemo
deployment env directory. Scrub the existing TaskHeapDemo build if
you have one, then build.
cd /general/sand/deploy/TaskHeapDemo/env
cp /general/gwt-mac-1.4.62/gwt-servlet.jar .
cd ../build
ant scrub
ant
The lines we modified tell the build how to create the files we need for our GWT interface. The first line lets the build know that we have a second entry point for the GWT service and that all the files from GWT can be found in the gwt directory for the webapp. The second line lets it know that we will be needing the GWT servlet support, and the third line runs the generator to build what we need from the server side. The messages supported are those that are declared as being received by TaskHeapUINodeDecl, see the generator documentation for more info.
Now that we've created the files that GWT needs from SAND, we need to
let our GWT application know about it. Edit
gwt/src/org/TaskHeap/THMonitor.gwt.xml and tell it to
inherit sand.gwt.xml which will bring everything in:
<!-- Inherit the sand messages -->
<inherits name='sand'/>
We also need to declare the servlet that will be handling the
requests. Again within the THMonitor.gwt.xml module:
<servlet path="/sandgwt"
class="org.TaskHeap.server.SANDMessageServiceImpl"/>
At this point we can test running the application locally within
the GWT environment using the local SANDMessageServiceImpl
that was generated for testing purposes. First compile the stub server
within the GWT environment and then run it in the shell:
cd /general/sand/deploy/TaskHeapDemo/gwt
ant -f THMonitor.ant.xml
./THMonitor-shell
You should see the THMonitor page, with stats being displayed after the java code has been interpreted and begins executing.
To run the GWT interface from the SAND server, we need to compile everything to javascript and put it into the TaskHeapDemo build:
./THMonitor-compile
cd /general/sand/deploy/TaskHeapDemo/webapp
mkdir gwt
cd gwt
cp ../../gwt/www/org.TaskHeap.THMonitor/* .
THMonitor-compile creates the compiled script and other supporting
files we need and places them into
the www/org.TaskHeap.THMonitor directory. We copy these
to the webapp/gwt area in the SAND build so they will be
found in the logical place as the servlet that was generated from the
sandui declaration name.
Build and run:
cd /general/sand/deploy/TaskHeapDemo/build
ant
You should now be able to see the standard application come up at http://localhost:8080/THD_TaskHeapDemo/TaskHeap, and the monitor app come up at http://localhost:8080/THD_TaskHeapDemo/gwt/THMonitor.html.
java.io.Serializable.
While the generated messages are Serializable, the default
serialization may ignore the inherited struct fields unless the struct
is also Serializable. Whether this is a serialization bug or not is
arguable, but having seen the struct data not get transferred by GWT
this explicit declaration is probably a good idea.