Skip to content

All posts by moore - 81. page

Step #2b: Updating the GWT Client to communicate with the Server

The next step is to update the client to communicate with the server side components. Again, if you’re not very patient, you can see the live client code at Colorado HomeFinder, and download the source.

I need to add a drop down, which will offer users the option of a 5/25 ARM rate, or a 30 year fixed rate. I need to have some way of decoding the JSON response from the server, and I need to update the interest rate text box with the results from the server.

Adding the drop down is fairly easy: Google provides a listbox. The listbox is an instance variable, rather than a local variable, and I also change the interest text box from a local variable to an instance variable. The reason: the change listener, which is a non static member class, has easier access to an instance variable than to a local one. Here’s the relevant code:

rateChoiceBox.addItem("5/25 ARM");
rateChoiceBox.addItem("30 Year Fixed");
rateChoiceBox.setVisibleItemCount(1);
rateChoiceBox.setWidth(width);
final RatesResponseHandler rrh = new RatesResponseHandler();
HTTPRequest.asyncGet(RATES_URL, rrh);

rateChoiceBox.addChangeListener(new ChangeListener(){
public void onChange(Widget sender) {
HTTPRequest.asyncGet(RATES_URL, rrh);
}
}
);
...
private class RatesResponseHandler implements ResponseTextHandler {

public void onCompletion(String responseText) {
boolean keepGoing = true;
JSONObject jso = null;
try {
jso = JSONParser.parse(responseText);
} catch (JSONException je) {
keepGoing = false;
// not sure what to do
}
String rate = "";
// ... get the data, put it into the rate variable.
MortgageCalc.this.interest.setText(rate);
}
}

In the RatesResponseHandler, I try to parse the JSON response that has been asynchronously downloaded from the server, and if I get meaningful text, I set the interest rate in the textbox: MortgageCalc.this.interest.setText(rate); If the server is not available, we still have a sane default. One interesting item: when you run the code with the server off in development, GWT pops you into the MS script debugger, which at least lets you know something is wrong.

The JSON processing code has already been written, so it made sense to leverage the JSON example that Google kindly provided. To do this, I made a few changes. Luckily, all the example code is made available under the Apache 2.0 license. You can find json.jar in the download, should you want to use it for a project of your own.

First, I wanted to make sure that none of the behavior of the EntryPoint JSON class occurred–I wanted the subsidiary libraries, but not the potato demostration. Therefore, I eviscerated the JSON class, and gave it an empty onModuleLoad method. On reading the module documentation, it appears this wasn’t needed–I could have just eliminated the <entry-point> entry.

Then, I started building the JSON code library. To pull in a set of external widgets, you need to do a couple of things.

  1. Build the library. I did this by compiling the JSON source, from the samples directory: mkdir builddir && javac -d builddir -classpath `cygpath -wp $PWD/bin/:.:../../../gwt-windows-1.0.21/gwt-user.jar` src/com/google/gwt/sample/json/client/JSON*.java. (The cygpath stuff is there because I’m developing on cygwin.) One thing to remember–you need to copy the java files into that same build directory: cd builddir/com/google/gwt/sample/json/client && cp ../../../../../../src/com/google/gwt/sample/json/client/*.java .. (Apparently you need the .class files for Eclipse or any other IDE, and you need to .java files so the Google compiler can compile the java to javascript. More here.) Also, don’t forget the JSON.gwt.xml, which should go in builddir/com/google/gwt/sample/json.
  2. Jar up the code, and place it in the classpaths. To compile from the command line, I added it to the Mortgage-compile.cmd batch file. For Eclipse development, I added it to the project buildpath (instructions for doing so). And I also had to add the json.jar file to the MortgageCalc application, so I could debug in Eclipse. To do this, choose ‘Run’ from the top menu, choose the ‘Run…’ option, select ‘MortgageCalc’, click the ‘Classpath’ tab, click ‘User Entries’ and add the jar using the Add button.
  3. Inherit from the JSON module. As far as I can tell, this isn’t inheritance in the usual object oriented sense, it’s more like importing packages in java. I did this by adding this line to the MortgageCalc.gwt.xml: <inherits name='com.google.gwt.sample.json.JSON'/> (I did try to use a source path to refer to the JSON classes, rather than inherit from them, but got this error:

    [WARN] Non-canonical source package: ../../../google/gwt/sample/json/client/

    [ERROR] Unable to find type ‘com.cohomefinder.gwt.mortgagecalculator.client.MortgageCalc’
    Hint: Check the inheritance chain from your module; it may not be inheriting a required module or a module may not be adding its source path entries properly

    )

After going through those steps, I could use the JSONParser and other JSON objects in my widget. Here’s the balance of the RateResponseHandler:

if (keepGoing && jso != null) {
int selected = MortgageCalc.this.rateChoiceBox.getSelectedIndex();
String [] keys = jso.getKeys();
if (keys.length == 2) { // expected
JSONValue jsvrate =
jso.get(keys[selected]);
JSONString jsstr = null;
if ((jsstr = jsvrate.isString()) != null) {
String rate = "";
if ((rate = jsstr.toString()) !=
null) {
//Window.alert("rate:
//"+rate);
MortgageCalc.this.interest.setText(rate);

}
}
}
}

The JSON parsing code is rather verbose. I also wasn’t a fan of the return values from the is* methods; I was always told that any method prefixed with ‘is’ should return a boolean. But then I peeked into the JSONParser class and decided that I’d just use the free code
and stop complaining. Final comments:

  • The module inheritance system is a quite powerful, if slightly
    misnamed, method of reusing code. In fact, there is already at least one blog about GWT widgets.
  • Packaging up code for a module is tedious, and perfectly suited to an
    ant task.
  • HTTPRequest is entirely adequate for pulling down data.
  • JSON, combined with HTTPRequest, gives you some typing, without tying
    you into a custom servlet. If you want typing stricter or more granular
    than javascript
    provides
    and are using a java backend, then Google’s
    services
    might be a better choice.

Step #2a Creating the Server side components

After choosing the means for transmitting the data, the next step is to build the server side code. For Expresso, that means creating a new controller and a new state. A controller is similar to a Struts action; one controller may have many states. Please be aware that if you want to play with the new client code, you can use almost any dynamic web aware language, or even a static HTML page, to return the appropriate JSON, which will look similar to this: {"fivearm":"5.00","thirtyfixed":"6.50"}.

Adding a new state to handle a request for mortgage interest data in Expresso is not too complicated, though it could definitely be easier. I needed to:

  1. Add the values to the database. Expresso has a setup table for generic configuration information that I’ll use.
  2. Create a class that extends DBController, and have this class access the database and create the JSON.
  3. Update the configuration file to map a URL to a class. This version of Expresso is based on Struts.
  4. Add a JSP for output.
  5. Update the security tables so that this state can be accessed by everyone.

Adding the values to the database is just an insert statement. For the thirty year fixed interest rate, the SQL looks like this: insert into SETUP values ('com.cohomefinder.CoHomeFinderSchema', 'MTG_30_Year_Fixed_Rate','30 Year Fixed Mortgage Interest Rate','6.50');. One of the benefits of using the setup table is that Expresso ships with an Administrative Web Interface which lets non technical users change ‘setup values’ with a browser.

The next step is to create a class to respond to our request. That class is the MortgageCalculator controller. This is a fairly simple class, which uses the JSON.simple Java library to create a correctly formatted JSON object. Note that the rates are sent back as Strings even though JSON can handle converting to numbers. The reason for this is that I wanted some kind of formatting control; if the class sent back ‘6.5’ for the thirty year fixed interest rate, I might want it formatted as ‘6.50’. Formatting is simpler on the server, where I have the entire Java API to use, including NumberFormatter.

After that, I needed to create an entry in the configuration file to map some URL to this class.

<action path=”/MtgCalc” type=”com.cohomefinder.controller.MortgageCalculator” name=”default” scope=”request” validate=”false”>
<forward name=”getRatesAsJSON” path=”/expresso/components/registration/jsp/register/mtgcalc.jsp”/>
</action>

As you can see, this is very similar to configuring a struts-config.xml entry.

The JSP was extremely simple. It imports the expresso tag libraries in the standard manner, and then write the property that the MortgageCalculator class puts in the response (on the last line of the Java file).

<expresso:IfElementExists name=”JSON” type=”Output”><bean:write property=”JSON”/></expresso:IfElementExists>

The last thing to do is update the controller table to allow anyone to access this class. Again, it’s a simple SQL statement: insert into controllersecurity values ('com.cohomefinder.controller.MortgageCalculator','Everybody','*');. If I wanted to, I could restrict this information to certain classes of users, but in this case, everyone should have access to it.

After these steps, I can hit http://localhost:8080/MtgCalc.do?state=getRatesAsJSON and get back valid JSON containing values stored in the database.

The next step is to update the client to access the server data using HTTPRequest and to change the GUI accordingly.

Step #2: A Calculator which retrieves data from a Java server process

(Updated 6/14 with links to the two parts of this step)

The next step in building a real world mortgage calculator using GWT is to retrieve something simple from a back end. (See the problem’s introduction and Step #1, as well as the client code and the server code portions of the step described below.) It will be easiest to start with something simple. In this example, the client will pull two different mortgage interest rates (a 30 year fixed rate and a 5/25 ARM rate) from the Expresso back end. Since I am accessing a Java back end, there are two options.

  • Google’s services infrastructure. The benefit of this is fairly painless and transparent marshaling of Java value objects–that’s according to the documentation, I haven’t used it. The main downside is that I can’t use Expresso’s default servlet (with its authentication, logging, and caching) to handle the service request, because you must always extend Google’s RemoteServiceServlet.
  • Use the HTTPRequest
    class. This class essentially wraps the XMLHttpRequest object in a cross browser way, so if you’re familiar with that Javascript construct (which is at the heart of AJAX), the API shouldn’t be too shocking. This class limits the types of response available; no XML/DOM tree is passed back from a request, just text (in a String). The benefit of this method is that it’s very familiar to folks who’ve used XMLHttpRequest and is relatively simple. The main downside is that you’re limited to Strings as return values. There are, however, ways around that limit.

Based on the current requirements, it made more sense to use HTTPRequest than Google services. I could see using the Google services layer if I were doing some green field development in Java, or in a situation where such transparent marshaling saved a significant deal of work.

Of course, when you’re sending back text, you have a couple of options for encoding the data. I considered a custom encoding, but that’s not very scalable to large datasets, escaping and unescaping can be non trivial, and there are well known text transfer formats out there. Of those I know, XML and JSON are the primary ones. I went with JSON because Google kindly (almost) provides a JSON parsing library in their sample code. This parsing library is nice because it allows me to send back values as Strings, Arrays, Booleans, Numbers or Objectss and converts the JSON to the appropriate type. I say almost because I had to make a few changes to their code.

In my next post, I’ll look at the server side changes I made, including integrating the JSON.Simple Java library into Expresso.

Step #1: A Calculator with No Server Interaction

(Update 6/26: Here’s step #2, step #3 and the conclusion, with more source code.)

The first step is to build a simple javascript mortgage calculator, like many others out there, but using GWT. It seems that GWT is a great way to build complicated JavaScript UIs without much knowledge of JavaScript. (As an aside, I know this first solution is vastly overengineered, but eventually the problem set will require more of GWT.)

If you’re in a hurry, the GWT Mortgage Calculator is live and here is the source (for eclipse, for Windows; you’ll need to download the GWT and place the libraries in the lib subdirectory).

First, I found this article about calculating loan payments, which seemed straightforward enough. Then I downloaded the GWT and played around with some of the examples. I then created a project for eclipse, using the instructions in the GWT getting started guide.

A mortgage calculator is a pretty simple problem, really. Five labels and text boxes cover the needed inputs and outputs for interest rate, loan amount, term, number of payments and paymount amount. Also needed is a ‘calculate’ button and some way of conveying an error, should the user give invalid input.

I looked at using HorizontalPanel and VerticalPanel, but they looked horrible. I ended up using a FlexTable, which worked just fine.

I wanted to do some inline validation on the textboxs to make sure no one was entering anything other than numbers and a decimal point. Luckily, there is sample code for doing just this. This code worked just fine when I was running in my hosted environment, but when I deployed to a server and looked at the calculator in FireFox, I couldn’t type anything. The Google Web Toolkit group had the answer:

The problem is that GWT does not handle the the way that Mozilla/Firefox uses keyCode. For a key press event, evt.keyCode is only set for the non-character codes (Function keys, PageUp etc). For character keys the evt.charCode field is set. For key up/down events evt.charCode is never set, only evt.keyCode.

After disabiling this input validation, the calculator worked on FF.

I started out using a popup for error conditions, like non numeric user input. This examples is wrong–rather than setWidget() as the last line of the constructor, you need add(), otherwise you end up seeing an error like this:

[ERROR] Uncaught exception escaped
java.lang.RuntimeException: JavaScript method '@
com.google.gwt.user.client.ui.impl.PopupImplIE6::fixup
(Lcom/google/gwt/user/client/Element;)'
threw an exception
at com.google.gwt.dev.shell.ie.ModuleSpaceIE6.invokeNative(
ModuleSpaceIE6.java:394)
at com.google.gwt.dev.shell.ie.ModuleSpaceIE6.invokeNativeVoid(
ModuleSpaceIE6.java:283)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(
JavaScriptHost.java:127)
at com.google.gwt.user.client.ui.impl.PopupImplIE6.fixup(
PopupImplIE6.java:43)
at com.google.gwt.user.client.ui.PopupPanel.show(PopupPanel.java:211)

(More information here.) I ended up using a dialog box instead, because I felt it looked better.

Now I had a working copy of the MortgageCalculator, and I just needed to integrate it into my client's website. For speed, we decided I'd just check the derivative javascript/html/xml into the site's source tree. Obviously, for the long term this is not a viable solution. A better way to do this would be to store the java files (and libraries, etc) in CVS and use some of the ant integration out there. Eventually we'll get there.

For ease of integration, I added a paragraph element to an existing JSP with a unique ID that the java class will reference and into which the java class will push the generated html (updated 6/13 to fix a broken url). This is just like the sample application.

In order to access the javascript when the including file and the generated javascript are not in the same directory, you need to not only modify the src attribute from <script language="javascript" xsrc="gwt.js" mce_src="gwt.js"></script> to <script language="javascript" xsrc="/absolute/path/to/gwt.js" mce_src="/absolute/path/to/gwt.js"></script> but also the meta tag. This tag changes from <meta name='gwt:module' content='com.cohomefinder.gwt.mortgagecalculator.MortgageCalc'> to <meta name='gwt:module' content='/absolute/path/to/com.cohomefinder.gwt.mortgagecalculator.MortgageCalc'>. This tag can actually be placed in the body rather than in the head.

I ran into another issue when integrating this code into the existing website. For some reason, in FireFox 1.5.0.4 the error dialog box doesn't show up where it should, and instead is placed at the bottom of the screen. Here is the relevant section of code:

if (retVal != 0) {
ErrorDialog err = new ErrorDialog("v1 Please enter all data in n
umeric format.");
Widget slot1 = RootPanel.get("slot1");
int left = slot1.getAbsoluteLeft() + 10;
int top = slot1.getAbsoluteTop() + 10;
err.setPopupPosition(left, top);
err.setStyleName("error-Popup");
err.show();
}

and here's the error message from the Javascript Console:

Error: Error in parsing value for property 'left'.  Declaration dropped.
Source File: http://localhost:8080/Colorado-mortgage.htm Line: 0
Error: Error in parsing value for property 'top'.  Declaration dropped.
Source File: http://localhost:8080/Colorado-mortgage.htm Line: 0

This seemed to occur no matter which widget I was trying to get the absolute position of--the button, the enclosing table, or the enclosing paragraph element. This only occurred when integrating the calculator into a JSP page (not in the generic generated html), so it's likely it was a site specific bug. To work around it, I used a label rather than a dialog box.

Other random tidbits:

  • In general, I was less than impressed with the Google provided documentation, but very impressed with the GWT group.
  • It was very easy to quickly develop using Eclipse and the hosted web browser.
  • The HTML of the file you're developing to lives under the src directory (all the way down in the public folder).
  • All the javascript and html are compiled into the www directory.

Next, we're going to integrate with Expresso and pull some values from the database.

Step by Step: A Mortgage Calculator using GWT

The following couple of posts are a first for my blog. I have blogged about client projects before, but never have I been paid to do so. But, I currently have a client who enjoys my blog. He also is a big fan of the Google Web Toolkit, and is interested in exploring the usefulness of this toolkit to his website, Colorado HomeFinder (Updated 6/10 with the name of the client, per his request). (Disclaimer: everything I say on this blog is my fault and mine alone.)

So, in the interest of exploration, I’m going to be building a simple mortgage calculator. We’ll be using GWT 1.0.21, developing on Windows XPSP2 and integrating with a existing backend built using Expresso 5.3, a heavyweight open source framework. (I’ve used Expresso before, and written about it, and while the framework is not without its warts, it can be very useful.) I will also be using Eclipse 3.1 to build the code, and be deploying to Linux. FireFox 1.5.0.4 and IE 6.0 (both on Windows XPSP2) will be used to test the application.

I’ll be documenting my missteps and lessons learned as I go. In addition, the client has kindly offered to let me distribute the source on my website, so I’ll be providing a download each step of the way.

Update, Aug 9: Here’s a list of what I’ve written:

Google does spreadsheets

Check out spreadsheets.google.com. Limited time look at what javascript can do for a spreadsheet. I took a quick look and it seems to fit large chunks of what I use Excel or calc, the OpenOffice spreadsheet program, for. Just a quick tour of what I such spreadsheet programs for, and what Google spreadsheet supports:

  • cut and paste, of text and formulas
  • control arrow movement and selection
  • formatting of cells
  • merging of cells and alignment of text in cells
  • undo/redo that goes at least 20 deep
  • sum/count
  • can freeze rows
  • share and save the spreadsheet
  • export to csv and xls

On the other hand, no:

  • dragging of cells to increment them (first cell is 45, next is 46, 47…).
  • using the arrows to select what goes into a formula–you can type in the range or use the mouse

Pretty decent for a web based application. And it does have one killer feature–updates are immediatly propagated (I have never tried to do this with a modern version of Excel, so don’t know if that’s standard behaviour). Snappy enough to use, at least on my relatively modern computer. I looked at the js source and it’s 55k of crazy javascript (Update, 6/9: This link is broken.). Wowsa.

I’ve never used wikicalc but it looks more full featured that Google spreadsheets. On the other hand, Google spreadsheets has a working beta version…

This and the acquisition of writely make me wonder if some folks are correct when they doubt that Google will release a software productivity suite. (More here.) Other interesting comments from Paul Kedrosky.

I know more than one person that absolutely depend on gmail for business functionality, which spooks me. And in some ways, I agree with Paul, it appears that Google “…takes a nuclear winter approach wherein it ruins markets by freezing them and then cutting revenues to zero.”

Personally, if I don’t pay for something, I’m always leery of it being taken away. Of course, if I pay, the service can also go away, but at least I have some more leverage with the company–after all, if they take the service away, they lose money.

Bloglines and SQL

I moved from my own personal RSS reader (coded in perl by yours truly) to Bloglines about a year ago. The main reason is that Bloglines did everything my homegrown reader did and was free (in $ and in time to maintain it).

But with over 1 billion articles served as of Jan 2006, I always wondered why Bloglines didn’t do more collaborative filtering. They do have a ‘related feeds’ tab, but it doesn’t seem all that smart (though it does seem to get somewhat better as you have more subscribers). I guess there are a number of possible reasons:

  • It’s easier to find feeds that look like they’d be worth reading (I have 180 feeds that I attempt to keep track of)
  • blogrolls provide much of this kind of filtering at the user level
  • privacy concerns?
  • No demand from users

But this article, one of a series about data management in well known web applications, gives another possible answer: the infrastructure isn’t set up for easy querying. Sayeth Mark Fletcher of bloglines:

As evidenced by our design, traditional database systems were not appropriate (or at least the best fit) for large parts of our system. There’s no trace of SQL anywhere (by definition we never do an ad hoc query, so why take the performance hit of a SQL front-end?), we resort to using external (to the databases at least) caches, and a majority of our data is stored in flat files.

Incidentally, all of the articles in the ‘Database War Stories’ series are worth reading.

Using Grids?

Tim Bray gives a great write up of Grid Infrastructure projects. But he still doesn’t answer Stephen’s question: what is it good for?

I think the question is especially relevant for on demand ‘batch grids’, to use Tim’s terms. A ‘service grid’ has uses that jump to mind immediately; scaling web serving content is one of them. But on demand batch grids (I built an extremely primitive one in college) are good for complicated processes that take a long time. I don’t see a lot of that in my current work–but I’m sure my physics professor would be happy to partake.