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)
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.
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