JBoss Community Archive (Read Only)

RHQ 4.9

GWT Tips

Overview

When developing in GWT, you write Java code that is "compiled" into javascript that will run in the browser. GWT has the concept of classes for client side, server side and those shared between them. It has some limitations over straight java (e.g. lack of reflection and Class.getSimpleName).

Our core/domain module and enterprise/gui/coregui modules are gwt compiled. This allows us to write code that utilizes our domain classes right in the browser. We use GWT-RPC to call the server from the browser. These are the *GWTService interfaces and *GwtServiceImpl implementations. We also have to hibernate detach before we can serialize out to the browser. For now we have SerialUtility.prepare to do this though we may try out the gilead project for this.

We're not exposing the full remote APIs. A lot of the prototyping has been accomplished with the criteria APIs instead of the specialized calls. I want to prefer the criteria APIs and manually choose the extra set to expose to the GWT client side. This will help us pick a good proper set of APIs for a future more limited and managed remote API.

To run the prototype, do a full clean build of the gwt branch and start the server, then go to http://localhost:7080/coregui/CoreGUI.html

For development, utilize the "dev-mode" model of GWT development. In this model, you build and start the server as normal, but then separately start a GWT codeserver that will let you do edit-save-refresh development on the client side code. After starting the server, run "mvn gwt:run" or "mvn gwt:debug". The latter will start with jpda so that you can connect intellij or eclipse for debugging sessions on the gwt code. Yes, it let's you step through "browser-side code" in your java debugger. Yes, that is as cool as it sounds. When you run these, it'll start a small UI for dev mode. You can click "Launch Default Browser" to have your browser go to the proper URL for development. This will live "compile" the java gwt code so that you can see your UI.

We are using the SmartGWT library to build our UI. This is a GWT module that happens to layered on top of a JavaScript library called SmartClient. It is a rich MVC component library for typical things like tables, forms, etc. Being a client side MVC library, much of our data interaction code happens in the way of subclasses of RPCDataSource. These classes load data for things like tables and forms and can paginate, sort, update, delete, save, etc. in cooperation with the GWT-RPC services. Look at existing examples (e.g. RolesDataSource) for how this is done.

SmartGWT is a fairly active project so you can often find good info in the SmartClient.com forums or elsewhere. A very good model for development is inspecting the SmartGWT showcase at http://www.smartclient.com/smartgwt/showcase/

We're using SmartGWT for layout as well as our components. This means the layout is done more like with Swing than as HTML. SmartGWT does layout management and resizing and then sets the DOM elements via absolute positioning. This makes it somewhat more cross-browser compatible, but takes some getting used to if your mostly used to HTML output. It also means that it is often appropriate to have custom components or composite panels start by extending SmartGWT classes like HLayout,VLayout and Canvas.

With our components all being GWT classes though we can get a fair amount of reuse and build useful base classes and utilities. As soon as you start to see boiler-plate code think of refactoring.

GWT development is not the same as Swing or TCL-TK. We're still running JavaScript in a browser against a  DOM. We have to take into account backwards-forwards buttons, bookmarking, session cookies, etc. The biggest of these is the History management. We try to have most of our information pages bookmarkable against a URL. Our GWT app never actually changes the browser "page". Every different view is simply ajax and dhtml manipulation of the DOM. But we also manipulate the browser's url token (the part after the #). That means the url stays: /coregui/CoreGUI.html throughout the app, but you'll see it switch to things like "/coregui/CoreGUI.html#Resources/10001/Monitor/Traits". That sort of path refers to a specific state of the application with certain views displayed. The only views in the app that should not require separate history tokens are things like wizards and modal dialog boxes. This also means that simply rendering a new view in response to a listener is generally the wrong thing to do. (where the right thing to do is fire a history event that will propagate rendering the new views)

Gotchas

  • GWT compiler doesn't like enum declarations that don't have a semicolon after them... not required by javac, but needed for gwt

  • GWT compiles create javascript rpc declaration files... if they're stale or there are extras you'll get errors about serialization... run "mvn gwt:clean install" to get a fresh gwt compile

  • The coregui.war web only has to be reloaded if you change serialization classes or the RPC interfaces... Easiest way to redeploy is "mvn gwt:clean install"... (A full clean will not properly redeploy in jboss)

  • If you change a domain class that is gwt serialized you can run "mvn gwt:clean install" in both domain and coregui to update... may have to restart if it affects server side code

  • SmartGWT sometimes eats exceptions... Use "mvn gwt:debug" and attach a jpda debugger, then set exception break-points on at least NPE and "com.google.gwt.core.client.JavaScriptException"

  • Sometimes I add try - catch throwables fo easier debugging in problem areas

  • DynamicForm.getValue() takes a string which is the name of a FormItem.

Miscellaneous Notes

  • keep in mind, all calls to the server-side are asynchronous (this is a GWT thing)

  • top panels - Administration, Dashboard, etc. (some have left-navs)

  • SerialUtility.prepare() strips out all Hibernate objects of result objects so they can be serialized using GWT Remoting

  • cut down on unnecessary methods in EJB SLSBs - use criteria whenever possible (can even be used for composites)

  • ResourceTypeRepository - in-browser cache of type info - working great so far, but we should be careful of caching too much data

  • bookmarks are done by appending #Blah to base URL; hidden frame on webpage stores the history, so back and forward buttons work; use History.newItem() to go to a new view, rather than updating the view programmatically; in certain cases (e.g. switching from one Resource tab to another), we do update the view programmatically for optimization performance, but in those cases, we still call History.newItem(false) to update the history without firing a history event; each level of bookmarkable views must implement BookmarkableView which provides the event handler (renderView()) for history events (e.g. for the path #Administration/Security/Users, CoreGUI.renderView() is called to render the Administration portion (AdministrationView), then AdministrationView.renderView() is called to render the Security/Users portion inside the AdministrationView; breadcrumbs are integrated with the bookmark/history API; the only time you need to worry about the breadcrumbs is when you don't want the breadcrumbs to be exactly the same as the bookmark path items (e.g. for a Resource detail view, the bookmark will be #Resource/10001, but the breadcrumbs will be the resource's full lineage with names and types

  • for both static (e.g. traits or Resource general properties) and dynamic (config props) name-value pair data, use the DynamicForm class

  • do form validation synchronously when possible and then if there are errors, set the field-specific error messages and a summary error message on the DynamicForm, rather than using the message center

  • message center should be used for unexpected errors, non-validation errors and info messages, and things that are async calls on the server side (e.g. operations)

  • we use SmartGWT's MVC data binding framework (DataSources) for all our retrieval of data sets; we have some base classes on top of this stuff; it supports paging, sorting, filtering (using SmartGWT's own criteria, not to be confused with RHQ criteria), add item, and remove item

  • writing a DataSource

    • typically extend RPCDataSource base class

    • constructor should define all visible fields

    • implement executeFetch(), which maps the SmartGWT criteria to RHQ criteria, and then creates a set of SmartGWT ListGridRecords - one per result set item

    • one DataSource may be used by one or more ListGrids

    • always add enum.name(), rather than the enum itself, to a ListGridRecord!

  • writing a ListGrid

    • typically extend the Table base class, which wraps a ListGrid

    • on top of the sorting/paging/filtering provided by ListGrid+DataSource, the base class provides header, footer, select checkboxes, selection-aware buttons, and refresh button

  • writing a wizard

    • all wizards are popup dialogs

    • should validate forms at each step of wizard and show any errors on that page rather than proceeding to next step

  • other handy components

    • selector (left-right) components

    • config editor (no group config editor yet)

    • radio button show/hide panel component

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-13 08:20:01 UTC, last content change 2013-09-18 19:40:40 UTC.