With the new GWT 2.0 (due to be released this year?), they have add a couple of great features. The one I’m most excited about from a users perspective is RunAsync (aka code splitting). To quickly recap, this feature will split a GWT application into multiple developer controlled (split points) downloaded files. We are reworking our internal GUI architecture and have decided to use the unreleased GWT version for development. Our largest application before compression (with obfuscation) is weighing in around 1.4MB. That’s pretty hefty for downloaded javascript files.
We have limped along waiting for GWT 2.0 and code splitting. We didn’t want to put in a temporary solution of using URL redirects and manually splitting our code into modules for a 6 month solution. We’re happy we didn’t. With our refactoring effort, we have designed it so that we can easily have split points at our “module” level (basically a group of related screens).
We don’t have hard numbers yet for an apples-apples comparison, we like our first attempt. It looks like we will have three downloads to start our application. The first bundle is about 140K, the second which is a module is about 80K and a leftover module which is 500K. The leftover module is primarly framework items (both GWT – e.g., Button, and our internal framework code).
We expect, as we work with the new functionality, to be able to get these values to go down (particularly the third – leftover download). After all, we only have one split point right now. We also are probably going to put in a new “home” page that displays when the user (same for every user) logs in so that the other download bundles can be download in the background.
The trick with RunAsync is to get the use case correct. RunAsync as it’s name implies doesn’t execute code sequentially. Therefore we had to add a callback mechanism into our code to display the GUI that was just downloaded.
For those interested it looks something like:
public void retrieveViaCallback(final OurInternalCallBackGuiObject callback){
GWT.runAsync(new RunAsyncCallback(){
public void onFailure(Throwable err){
Window.alert("code download failure");
}
public void onSuccess(){
callback.show();
}
});
}
We haven’t dealt with error handling yet (hence the call to Window.alert); but the RunAsync is pretty easy to get going if you have good modularization in your code base already.
In reality it can’t be done with the stock code. In our application we would like to be able to “stop” an event from being fired further.
One example, we have three event handlers that can be added A, B, C (validation, required check, and save rpc). We add the handlers to a button in that order when needed (sub class determine which handlers to add). We want to be able to specify in A or B handler that the event should stop propagating to other handlers.
We tried a couple of different things (including starting to add our own event handling logic) and we settled on two changes to GWT itself. We added a method to GWTEvent:
- public void stop(); /* record that the event should not be passed to more handlers */
- public void isStopped(); /* is the event stopped for further processing */
- public void unStop(); /* reset the stop flag to be false – did in case event is reused */
In our event handlers we can now call event.stop() to stop further propagation.
In HandlerManager.HandlerRegister we modified
private <H extends EventHandler> void fireEvent(GwtEvent<H> event,
boolean isReverseOrder) {
Type<H> type = event.getAssociatedType();
int count = getHandlerCount(type);
if (isReverseOrder) {
for (int i = count - 1; i >= 0; i--) {
H handler = this.<H> getHandler(type, i);
event.dispatch(handler);
if(event.isStopped()) { // CUSTOM: event stop processing
event.unStop(); // did this in case events are reused
break;
}
}
} else {
for (int i = 0; i < count; i++) {
H handler = this.<H> getHandler(type, i);
event.dispatch(handler);
if(event.isStopped()) { // CUSTOM: event stop processing
event.unStop(); // did this in case events are reused
break;
}
}
}
}
While this is a very specific use case, hopefully with enough examples, some thought will be put into an extension mechanism for HandlerManager. Until then, we get to live with our solution.
EDIT: After a post on the GWT list, it looks like this type of use case (developer extensions for the event handling) where considered but deemed too much/too varied to include anything in the GWT 2.0 timeframe. Here is the discussion.
We have been using the gradle build system for some time (0.7 right now). In the process of updating some of our infrastructure pieces I decided to make the GWT compile process a bit more robust and include it in the gradle build (much more so than we had been doing). For the most part it is a very straight forward process. This blog by Trond Andersen lays out just about everything you need to know (I’ve posted it below with a few modifications just in case it disappears).
My biggest problem came when I tried to execute the compile and nothing happened. Actually, the console returned a Java System exit error of 1. Not very helpful. I tried a bunch of stuff to figure out what I was missing. Turns out logging anything from a forked process is a bit more difficult than not. And then gwt compiler uses a non standard logging mechanism. I finally found the option to pass to the compiler: -treeLogger
If you are having issues with your compile, this is a great parameter to know about. It will popup a new window with the error messages in a tree format – very nice. This works even when the compile process is run as a seperate jvm – which is key if you want to set the classpath. Turns out I was missing classpath entries which is a two second fix to add to the gwt dependency chain in gradle (see below).
Anyway, here is a modified version of the process with the treeLogger inserted but commented out:
usePlugin('war')
//cut standard war build file configuration
gwtBuildDir = 'some/path/to/an/output/directory/for/gwt/javascript'
task gwtCompile << {
created = (new File(gwtBuildDir)).mkdirs()
ant.java(classname:'com.google.gwt.dev.Compiler',
failOnError: 'true',
fork: 'true') {
jvmarg(value: '-Xmx184M')
arg(line: '-war ' + gwtBuildDir)
arg(line: '-logLevel INFO')
arg(line: '-style PRETTY')
// arg(line: '-treeLogger')
arg(value: 'me.trond.app.MyApp')
classpath {
pathElement(location: srcRootName + '/' + srcDirNames[0])
pathElement(path: configurations.gwt.asPath)
}
}
}
Add a configuration type just for the gwt classpath:
configurations {
gwt
}
This also had to be reflected in the dependencies:
dependencies {
// ...
gwt name: 'gwt-user', version: '1.6.4'
gwt name: 'gwt-dev-windows', version: '1.6.4'
// ... other dependencies
}
Make sure the gwtCompile task is called before packaging up the war file:
war.dependsOn gwtCompile
Also ensured that the result of the gwtCompile is included in the war file:
war {
//Adds the JavaScript and resources compiled by the GWT compiler
fileSet(dir: file(gwtBuildDir) )
}
I have a case where my gwt app loads in the client browser, performs an RPC call, receives the results and then stops. I have no clue as to why. Debuging on firebug and after the result (which show in the net panel and the script panel (firebug 1.4.0)) is returned, nothing is executed. There are no errors, and breakpoints are never tripped.
I am building a special debug version now and there is a way to add a generic exception handler. I had used this before and forgotten about it until someone pointed it out to me again.
A quick example…
//chain handlers
private UncaughtExceptionHandler ueh;
ueh = GWT.getUncaughtExceptionHandler();
GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
public void onUncaughtException(Throwable t) {
logToConsole("AppController: Uncaught exception handled: " + t.getMessage());
if (ueh != null) {
ueh.onUncaughtException(t);
}
}
});
I’ve “solved” this particular problem too many times. So I need to write down the “solution”.
The crux of the problem: users (naturally) want to enter UTF-8 data into a web form.
The answer is simple but there are lots of moving parts:
- The html page must be encoded in utf-8 (not sure about this one; but do it for consistence). In eclipse, you can edit the properties of the gwt html launch page and edit the file encoding to be UTF-8
- The content type of the html page should be UTF-8. In the head tag portion put: <meta http-equiv=”Content-Type” content=”text/html; charset=utf8″ />
- The database connection needs to be opened with UTF-8. Mysql connection string is: jdbc:mysql:///YOUR_DB?useUnicode=true&characterEncoding=UTF-8
- The table or column need to be in UTF-8. To change the encoding for a specific column use: alter table table_name change colum_name colum_name character set utf8
This is down an dirty. There might be something in your http server and/or application server that needs to be changed also.
Also when converting the mysql table, be careful. The conversion could cause funky characters to show up depending on what encoding you are coming from; but that is a much bigger subject than this quick little post….