|
|
GWT introduction | gwt webapp basics | gwt webapp features | practicalities
Last updated: September 2008
GWT (Google Web Toolkit) is different than the previous frameworks examined,
because it is geared towards allowing high interactivity on the client -- which it
does by translating Java into JavaScript, including Ajax calls. The possibility
of creating RIAs (Rich Internet Applications) has fueled explosive growth in
GWT during 2008.
|
|
|
introduction For the GWT version of WebOfContacts these technologies were used:
contactsdb. Then
download the WAR
(make sure to retain the .war extension)
from this site
into your Tomcat's webapps directory, and view the application in your browser at
http://localhost:8888/com.oranda.webofcontacts.gwt.WebOfContactsMain/WebOfContactsMain.html
(log in as admin/admin).
The full source is available for download as an Eclipse project.
|
|
|
gwt webapp basics First, note that if you are using the Eclipse platform, it is convenient to use the GWT plugin, which is called Cypal Studio. Follow the documentation to create GWT modules and run configurations. Cypal allows you to run your application in an embedded Tomcat server. When you do run your application, you will get the Google Web Toolkit Development Shell (below) and a toy browser where you can test and debug applications until you click on "Compile/Browse" to see the results.
The structure of a GWT application is unlike the structure of webapps under most frameworks. For instance, in a GWT application you will generally divide the core code into client and server packages. The client package contains code that can be translated by GWT into JavaScript. This includes most but not all of the Java standard libraries. The server package contains the services that the client can call asynchronously -- it can of course can use anything in Java. (By the way, the JavaScript that is created for the client is actually generated in multiple versions, and then at runtime only one version is chosen for a particular client - this is called deferred binding). For a GWT application you need to define at least one module. A module is defined in an XML file and defines the entry point of the webapp among other things. For example the core of WebOfContactsMain.gwt.xml looks like this:
<module>
...
<entry-point class="com.oranda.webofcontacts.gwt.client.WebOfContactsMain"/>
...
<servlet class="com.oranda.webofcontacts.gwt.server.ContactServiceImpl" path="/contactService"/>
<servlet class="com.oranda.webofcontacts.gwt.server.UserServiceImpl" path="/userService"/>
<servlet class="com.oranda.webofcontacts.gwt.server.UploadFileServlet" path="/uploadFileServlet"/>
</module>
Notice that the services used by the client are included. These are implemented as
regular servlets and not web services. The module can also inherit settings from other
modules (especially GWT's own User module) and specify the path of needed source.
In addition to the module file, there is a WebOfContactsMain.html file. There is generally
no template HTML and no tag libraries involved here. The file just includes CSS
styles and, crucially, a reference to the generated JavaScript for the module like this:
<script type="text/javascript" language="javascript" src="com.oranda.webofcontacts.gwt.WebOfContactsMain.nocache.js">The WebOfContactsGwt application is structured so that the entry point (WebOfContactsMain.java) delegates most functionality to a ScreenManager singleton. The screen manager
constructs the UI, maintains the list of screens, and manages authentication and authorization.
(A more complex UI could be separated into MVC parts.)
To give you an idea of what UI code looks like in GWT, here is a snippet which constructs the
general layout in a DockPanel containing a header, footer, and navigation bar.
DockPanel uiContainer = new DockPanel();
uiContainer.setStyleName("page");
uiContainer.add(this.headerArea, DockPanel.NORTH);
headerArea.setStyleName("header");
uiContainer.setCellHeight(this.headerArea, "80px");
uiContainer.setCellWidth(this.headerArea, "100%");
uiContainer.add(this.footerArea, DockPanel.SOUTH);
footerArea.setStyleName("footer");
uiContainer.add(this.navArea, DockPanel.WEST);
navArea.setStyleName("leftnav");
The actual screens are then dynamically loaded into the DockPanel.CENTER
space, which is basically all the space that is left over. Notice the call to
setStyleName: this ties each component in with the CSS code. Size and
offsets are specified by a mix of CSS information and Java setter calls. (This
duplication of function is perhaps a weakness of GWT. If you don't like CSS, you
can try the GWT designer
Eclipse plugin with a point and click interface for layouts.)
Each Screen object is responsible for making calls to
the server. These calls are remote procedure calls, so the acronym RPC
gets used a lot in GWT. They are also asynchronous calls.
In other frameworks, calls to the service layer are synchronous and
relatively straightforward. In GWT the client makes a call
over the network to the server and waits for a response. As a result
there is a lot of boilerplate code in making a service call. It looks
like this:
this.service = GWT.create(ContactService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) service;
endpoint.setServiceEntryPoint("contactService");
// define a handler for what to do when the
// service returns a result
this.callback = new AsyncCallback
ContactServiceAsync is an interface which duplicates the
declarations in the standard ContactService but adds on
a callback parameter to each method. This is simply what the GWT compiler needs and
expects. On the server there is an implementation: ContactServiceImpl.
In WebOfContacts the code actually looks a bit different to what there
is above. This is because I have abstracted out the boilerplate code
into a class called UIAction which specific service calls
subclass -- the Command Pattern. Admittedly it is still rather verbose
(and reminiscent of Struts 1) to have a new action class on the client
for every service call you want to make, but asynchrony in GWT comes at
a price.
In the layout code above there was a reference to the DockPanel
widget. GWT provides many other types of panel, and in fact a full set
of widgets including form controls. In a typical form you might have
a Grid with TextBox objects in the cells and
a submit Button at the end. The Grid is added
to a FormPanel
and a FormHandler is attached to the FormPanel.
The FormHandler has an onSubmit method, in which
a call to the service layer is made via the usual action class.
|
|
|
gwt webapp features
There is no widely-used plugin for form validation:
you just implement it yourself. In fact you don't really need anything
special to translate your validation code into JavaScript as with other
frameworks, because your client-side code is already being translated into
JavaScript. In other words you can use the same Java code on the server
side and client side. However, you cannot easily use |
|
|
practicalities Because of the asynchronous nature of calls in GWT, there is special support for testing. Basicallly your JUnit test has to extend GWTTestCase and define a widgetSetup() method
independently of the normal JUnit setUp(). widgetSetup() loads
the module, and needs to be called explicity by every test method.
public class WebOfContactsMainTest extends GWTTestCase {
private void widgetSetup() {
// Build the web GUI.
WebOfContactsMain webOfContactsMain = new WebOfContactsMain();
webOfContactsMain.onModuleLoad();
}
...
}
To support asynchronous calls, the key testing concept is the
Timer: it waits for your call to complete. For instance:
// Perform the call to be tested
screenLogin.getForm().submit();
Timer timer = new Timer() {
public void run() {
// on login the curScreen should be set to Home
boolean atHome = ScreenManager.getInstance().isCurrentScreen(
ScreenHome.LINK_NAME);
assertTrue(atHome);
finishTest();
}
};
timer.schedule(3000);
delayTestFinish(5000);
So, what is the final verdict on GWT?
I cannot give a final verdict
because it feels like it is still very much in development. In fact
it was only in 2008 that GWT started supporting Java 5. It is noteworthy
that GWT still only supports a subset of the Java API on the client-side and
this occasionally necessitates workarounds. For instance, you can't use log4j
or java.util.logging on the client-side; you need to use the simple GWT.log()
or a special GWT library.
GWT has a very attractive premise: you can make your web applications interactive without having to know JavaScript. You can code it in Java and debug it in Java using a tool like Eclipse. You don't have to worry about all the browser bugs and incompatibilities because GWT abstracts that away for you... That is, assuming that GWT is itself bug-free and does not require you to go debugging the thousands of lines of generated JavaScript code with a tool like Firebug. I did not have to do that with a simple application like WebOfContacts. However it seems GWT has not really been put to the test with a really large website, and you have to wonder how well it would scale. GWT is based around a single actual web page and it does not integrate well with other frameworks, so at the current time many consider it primarily good for single-function 100% AJAX web sites. Another issue is connecting GWT applications to traditional databases. Most people will use an ORM like Hibernate, and want to be able to access domain objects on the client-side. However, it turns out that the proxying/lazy loading approach taken by Hibernate tends to confuse the Java->JavaScript compilation process. In order to avoid compiler errors you need to use the plug-in hibernate4gwt, ensuring all your services extend fromHibernateRemoteService,
and that you load the HibernateBeanManager.
(This is the approach taken by WebOfContacts, and it works. The services rely
on custom DAO classes injected by Spring. The DAOs use Spring's
HibernateTemplate in the normal way.)
Google has taken on a significant challenge
here, and it should not be surprising that GWT still feels a bit awkward, even
as it tries to turn the Web into a rich client platform. As Google architect
Josh Bloch remarked, "It's not that the dog talks beautifully, it's that the
dog talks!" There is a good chance that either GWT or another framework with the
same idea will become a standard choice for web development. In the meantime
here is the documentation.
|