Google Web Toolkit: a case study

117
RJUG : 12-Mar-2012 Bryan Basham – The Google Web Toolkit Slide 1 © Copyright 2013, Software Alchemy Getting started Building Views Extending GWT Talking to the Server Building Presenters Using Data Grids Google Web Toolkit The Google Web Toolkit Bryan Basham Software Alchemy [email protected] http://www.linkedin.com/in/SoftwareAlchemist

description

"Google Web Toolkit" presents a case-study in GWT v2 development. This is an introductory to intermediate talk that looks at solid practices for developing a rich Web GUI within the context of a Spring v3 backend architecture. In the talk Bryan will present: * Introduction to GWT (basic demo app) * Building View * Building Presenters * Talking to the Server * Extending GWT * DataGrids in GWT

Transcript of Google Web Toolkit: a case study

Page 1: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 1

© Copyright 2013, Software Alchemy

Gettingstarted

BuildingViews

ExtendingGWT

Talking tothe Server

BuildingPresenters

UsingData Grids

GoogleWeb Toolkit

The Google Web Toolkit

Bryan BashamSoftware Alchemy

[email protected]

http://www.linkedin.com/in/SoftwareAlchemist

Page 2: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 2

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

DEMO #1

Gettingstarted

BuildingViews

ExtendingGWT

Talking tothe Server

BuildingPresenters

UsingData Grids

GoogleWeb Toolkit

Getting Started...

Page 3: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 3

© Copyright 2013, Software Alchemy

GWT Basics

● Single-Page Web Applications● Rich-client: phat without being fat● GUI code built in Java but translated into

JavaScript at build-time– Java classes represent HTML elements and

widgets– Rich client-side domain model using Java

POJOs● Client/server communication via OO-based

RPC; a lot like Java's RMI

Page 4: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 4

© Copyright 2013, Software Alchemy

Demo: “Hello World”

Page 5: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 5

© Copyright 2013, Software Alchemy

UserDatabase

Desktop

.................................

Internet Server

Server-sideComponents

& Entities

Client-sideComponents

& DTOs

Typical GWT Architecture

Page 6: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 6

© Copyright 2013, Software Alchemy

User

Boundary

RPC/async

DTO

RPC/impl

DatabaseDesktop

.................................

Internet Server

GWT Components

transfer objects (both directions)

reads

server calls GWT's serialization and RPC protocol

over Ajax/HTTP requests

user actions

generates HTML Other

Server-sideComponents

& Entities

Typical GWT Architecture

Page 7: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 7

© Copyright 2013, Software Alchemy

User

Boundary

RPC/async

Entity

DAO

DTO

RPC/impl

DatabaseDesktop

.................................

Internet Server

GWT Components Spring Components

«cre

ates

»

transfer objects (both directions)

delegate to CRUD

SQL

reads

server ca

lls GWT's serialization and RPC protocolover Ajax/HTTP requests

user actions

generates HTML Service

?

Typical GWT Architecture

Page 8: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 8

© Copyright 2013, Software Alchemy

User

Boundary

RPC/async

Entity

DAO

TransformerDTO

RPC/impl

DatabaseDesktop

.................................

Internet Server

GWT Components Spring Components

«cre

ates

»

transformsinto client-side DTO

delegate to CRUD

SQL

reads

server calls GWT's serialization and RPC protocol

over Ajax/HTTP requests

user actions

generates HTML Service

Typical GWT Architecture

Page 9: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 9

© Copyright 2013, Software Alchemy

Single-Page Web Apps

● All of the GUI is built with GWT Java code, which becomes JavaScript at build-time

● GWT provides a wide variety of panel widgets to hide complexity, such as:

– Using a DeckLayoutPanel to manage a deck of sub-panels in a wizard

– Using a TabLayoutPanel to manage a complex Domain model

● Use the RootLayoutPanel object to build a GUI that dynamically resizes as the browser resizes

Page 10: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 10

© Copyright 2013, Software Alchemy

SPWA Structure

● The basic SPWA structure of a GWT app:– A single HTML file– One or more CSS files– An “entry point” class– And the web.xml, of course.

Page 11: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 11

© Copyright 2013, Software Alchemy

Example: GWT Root Page

<!doctype html><html>

<head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link type="text/css" rel="stylesheet" href="MyApp.css"> <title>Web Application Starter Project</title> <script type="text/javascript" src="myapp/myapp.nocache.js"></script> </head>

<body>

<!-- OPTIONAL: include this if you want history support --> <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>

<!-- Main View of the app --> <div id='view'></div>

</body>

</html>

Page 12: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 12

© Copyright 2013, Software Alchemy

Example: Entry Point Class

public class MyApp implements EntryPoint { /** * This is the entry point method. */ public void onModuleLoad() { MyPresenter page = new MyPresenter(); RootPanel.get("view").add(page.getView()); }}

● A GWT app could have many presenter/views but typically you will want to create a top-level view for the whole SPWA.

● The MyApp is far too simple to need that, but the Alloy demo will demonstrate this architecture.

Page 13: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 13

© Copyright 2013, Software Alchemy

Model-View-Presenter

● Similar to the popular Model-View-Controller pattern, but cleaner separation:

– User interacts with the View– View interacts with the Presenter– Presenter interacts with the Model– Model provides:

● communication with the Server using RPC● entity representation using DTOs

User Presenter

RPC/async

DTO

reads

server calls

user actions

generates HTML View

signals events

sends data

Page 14: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 14

© Copyright 2013, Software Alchemy

Views

● A View generates the HTML of the page and transfers user actions to the Presenter

● A View is composed of three elements:– The UI-Binder config file (View.ui.xml)

declares the layout of the GUI's widgets– The Java file (View.java) provides the code to:

● handle modifying the view when the model changes● handle user events from the browser

– Any static support files, such as style sheets, images and other media

Page 15: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 15

© Copyright 2013, Software Alchemy

Example: View.ui.xml

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"><ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'>

<g:HTMLPanel> <h1>Web Application Starter Project</h1> <table align="center"> <tr> <td colspan="2" style="font-weight:bold;">Please enter your name:</td> </tr> <tr> <td id="nameFieldContainer"> <g:TextBox ui:field="nameField" /> </td> <td id="sendButtonContainer"> <g:Button ui:field="sendButton">Send</g:Button> </td> </tr> <tr> <td colspan="2" style="color:red;" id="errorLabelContainer"> <g:Label ui:field="errorLabel" /> </td> </tr> </table> </g:HTMLPanel>

</ui:UiBinder>

Page 16: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 16

© Copyright 2013, Software Alchemy

Example: View.java

public class MyView extends Composite {

interface MyUiBinder extends UiBinder<HTMLPanel, MyView> { } private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

@UiField Button sendButton; @UiField TextBox nameField; @UiField Label errorLabel;

private final MyPresenter myPresenter; private final GreetingDialogBox dialogBox;

public MyView(MyPresenter myPresenter) { this.myPresenter = myPresenter; this.dialogBox = new GreetingDialogBox(/* details skipped */); // createAndBindUi initializes fields initWidget(uiBinder.createAndBindUi(this)); // Focus the cursor on the name field when the app loads nameField.setText("GWT User"); nameField.setFocus(true); nameField.selectAll(); sendButton.addStyleName("sendButton"); }

// More code on next slide

Page 17: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 17

© Copyright 2013, Software Alchemy

Example: View.java (part 2)

public class MyView extends Composite { // More code on previous slide

public void displayError(String message) { errorLabel.setText(message); } public void showFailure(final String message) { dialogBox.showFailure(message); } public void showSuccess(final String message) { dialogBox.showSuccess(message); }

@UiHandler("nameField") void handleEnterKey(final KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { sendNameToServer(); } } @UiHandler("sendButton") void handleSendBtn(final ClickEvent event) { sendNameToServer(); }

// More code on next slide

Page 18: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 18

© Copyright 2013, Software Alchemy

Example: View.java (part 3)

public class MyView extends Composite { // More code on previous slide

private void sendNameToServer() { // First, we validate the input. errorLabel.setText(""); String name = nameField.getText(); if (!FieldVerifier.isValidName(name)) { errorLabel.setText("Please enter at least four characters"); return; } // Then, we send the input to the server. sendButton.setEnabled(false); dialogBox.reset(name); myPresenter.sendToServer(name); }}

Page 19: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 19

© Copyright 2013, Software Alchemy

Presenters

● A Presenter component converts User actions, via the View, into Server-side commands using an RPC component.

● A Presenter passes data from the Server to the Views, eg) a list of DTOs to a grid.

● Presenter class should be free of View-related aspects (no interaction with widgets):

– Makes the business-logic code cleaner– Permits simple unit-testing

Page 20: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 20

© Copyright 2013, Software Alchemy

Example: MyPresenter.java

public class MyPresenter {

/** Create a remote service proxy to talk to the server-side Greeting service. */ private final GreetingServiceAsync greetingRPC = GWT.create(GreetingService.class); private final MyView view;

public MyPresenter() { this.view = new MyView(this); }

public MyView getView() { return view; }

public void sendToServer(final String name) { greetingRPC.sayHello(name, new AsyncCallback<String>() { public void onFailure(Throwable caught) { view.showFailure(SERVER_ERROR); } public void onSuccess(String result) { view.showSuccess(result); } }); }}

Page 21: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 21

© Copyright 2013, Software Alchemy

Data Transfer Objects (DTO)

● DTOs provide a client-side representation of the application's Domain Model

– Basically just a JavaBean, with a no-arg ctor and properties with get/set methods

– Must implement the IsSerializable interface– Property values are limited to:

● Java primitives and wrapper classes● Most java.util classes (Date, ArrayList, etc)● Other GWT-serializable DTO classes

● Why not just use your Entity beans?

Page 22: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 22

© Copyright 2013, Software Alchemy

Remote Procedure Calls (RPC)

● RPC components provide communication with the Server, much like Java's RMI

● GWT's RPC mechanism requires:– The synchronous interface (MyRPC.java)– The implementation class (MyRPCImpl.java)– The asynchronous interface (MyRPCAsync.java)

● The Presenter makes calls to RPC components using the asych interface

– This is necessary because Ajax is used, which requires callback functions

Page 23: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 23

© Copyright 2013, Software Alchemy

RPC (Architecture Diagram)

Boundary «class»MyRPCImpl

server calls GWT's serialization and RPC protocolover Ajax/HTTP requests«class»

MyRPCAsync_impl

«interface»MyRPCAsync

GWT RPCservlet

delegates to

«interface»MyRPC

This class implementation is providedby GWT which contains boiler-plate codeto make Ajax requests.

GWT also provides a Servlet which:● Handles the RPC requests● De-serializes the arguments● Delegates the RPC call to the appropriate RPC implementation● Serializes the method return value

The Async interface is based upon theSync interface but with an additionalCallback<ReturnType> parameter.

Page 24: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 24

© Copyright 2013, Software Alchemy

Example RPC Component

● GreetingService interface:@RemoteServiceRelativePath("greet")public interface GreetingService extends RemoteService { String sayHello(String name) throws IllegalArgumentException;}

● GreetingService implementation class:public class GreetingServiceImplextends RemoteServiceServlet implements GreetingService { public String sayHello(String input) throws IllegalArgumentException { // Verify that the input is valid. if (!FieldVerifier.isValidName(input)) { throw new IllegalArgumentException( "Name must be at least 4 characters long"); } String serverInfo = getServletContext().getServerInfo(); String userAgent = getThreadLocalRequest().getHeader("User-Agent"); return "Hello, " + input + "!<br><br>I am running " + serverInfo + ".<br><br>It looks like you are using:<br>" + userAgent; }}

Page 25: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 25

© Copyright 2013, Software Alchemy

Example RPC Component (pt 2)

● GreetingServiceAsync interface:public interface GreetingServiceAsync { void sayHello(String input, AsyncCallback<String> callback) throws IllegalArgumentException;}

● web.xml configuration: <servlet> <servlet-name>greetServlet</servlet-name> <servlet-class>com.example.myapp.server.GreetingServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>greetServlet</servlet-name> <url-pattern>/myapp/greet</url-pattern> </servlet-mapping>

Page 26: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 26

© Copyright 2013, Software Alchemy

RPC (Interaction Diagram)

Boundary «class»MyRPCImpl

«class»MyRPCAsync_

impl

GWT RPCservlet

name:String“Fred”

result:String“Hello Fred”

callback:AsyncCallback<String>

sayHello(name, callback)

sayHello(name)

return

name:String“Fred”

result:String“Hello Fred”onSuccess(result)

GWT RPCinvoker

payload[sayHello|Fred]

payload[Hello Fred]

Page 27: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 27

© Copyright 2013, Software Alchemy

GWT Configuration

● A GWT project is organized into Java src and Webapp files in the war directory.

● The MyApp.gwt.xml file is theprimary config file.

Page 28: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 28

© Copyright 2013, Software Alchemy

GWT Configuration

<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.5.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.5.0/distro-source/core/src/gwt-module.dtd"><module rename-to='myapp'>

<!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/>

<!-- Inherit the default GWT style sheet. --> <inherits name='com.google.gwt.user.theme.clean.Clean'/>

<!-- Specify the app entry point class. --> <entry-point class='com.example.myapp.client.MyApp'/>

<!-- Specify the paths for translatable code --> <source path='client'/> <source path='shared'/>

</module>

Page 29: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 29

© Copyright 2013, Software Alchemy

GWT Configuration

● The Java code is roughly organized into three packages:

– client: on browser– server: on the server– shared: used on both sides

Page 30: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 30

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

Styles / CSS

Widgets Panels

RawBinding

UI-Binding

DEMO #1DEMO #2

Gettingstarted

BuildingViews

ExtendingGWT

Talking tothe Server

BuildingPresenters

UsingData Grids

GoogleWeb Toolkit

Building Views

Page 31: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 31

© Copyright 2013, Software Alchemy

DEMO #2

● Alloy is a Monitor & Control app on a document processing pathway:

● Also: search, reports, admin tools

document1

....

..

.....

....

Prospective Warehouse

UserDatamart1

Datamart2User

document2

....

..

.....

....

document3

....

..

.....

....

Page 32: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 32

© Copyright 2013, Software Alchemy

DEMO #2: Types of Pages

● This app contains these types of pages:– Dashboards: shows high-level status & system

health– Monitor: view detail status and content flow– Control: modify system properties and other

actions– Search: tools to search on document content

Page 33: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 33

© Copyright 2013, Software Alchemy

DEMO #2: Dashboards

Header

Footer

Bread-crumb

PagesDeck

Page 34: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 34

© Copyright 2013, Software Alchemy

DEMO #2: Monitor page

Page 35: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 35

© Copyright 2013, Software Alchemy

DEMO #2: Control page

Page 36: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 36

© Copyright 2013, Software Alchemy

GWT Widgets

● GWT provides basic HTML-based widgets– Button, Radio button, Checkbox, Listbox,

Textbox, Textarea, Hyperlink, and more● And some advanced widgets:

– DatePicker, ToggleButton, CellList, MenuBar, Tree, SuggestBox, RichTextArea, and more

● Rich table/grid components● Click here: Widget Gallery

Page 37: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 37

© Copyright 2013, Software Alchemy

Panels

● Panels are complex <div> elements with behavior

● Old-school panels to not handle browser resizing (and put the browser into quirks mode)

– HorizontalPanel, VerticalPanel, StackPanel, FlowPanel, DockPanel, PopupPanel, TabPanel

● GWT v2 provides modern, resizable panels– DockLayoutPanel, DeckLayoutPanel,

TabLayoutPanel, ScrollPanel, LayoutPanel– ...and DataGrid

Page 38: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 38

© Copyright 2013, Software Alchemy

Resize-able UI

● Fit the whole UI within the browser window● Use resize-able layouts from outside inward● Application.ui.xml:<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:alloy='urn:import:com.tr.cmg.alloy.ui.client.screen'>

<g:DockLayoutPanel unit="PX"> <g:north size="119"><alloy:Header ui:field="header" /></g:north> <g:center> <g:DockLayoutPanel unit="PX"> <g:north size="29"><alloy:Breadcrumb ui:field="breadCrumb" /></g:north> <g:center><g:DeckLayoutPanel ui:field="pageDeck" /></g:center> </g:DockLayoutPanel> </g:center> <g:south size="30"><alloy:Footer ui:field="footer" /></g:south> </g:DockLayoutPanel>

</ui:UiBinder>

Page 39: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 39

© Copyright 2013, Software Alchemy

«GWT Widget»Resize

Composite

«View»Abstract

PageViewview

1«Presenter»Abstract

Page

«View»Prospective

FeedLoadView

«Presenter»Prospective

FeedLoadPage

«GWT EntryPoint»Application pages

1..*

Extending this class forces each pageview to be resize-able. It is the developer'sresponsibility to decide how the page'slayout is constructed to support resizing.

... ...

Alloy's Core GUI Architecture

● Application contains many Pages with one View each:

Page 40: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 40

© Copyright 2013, Software Alchemy

Raw UI-binding

● GWT provides full access to the page's DOM– One-to-one methods for Element and Node APIs– Plus additional APIs to simplify common tasks,

like adding/removing CSS classes● GWT provides programmatic APIs to compose

higher-level widgets (which encode DOM Elements)

Page 41: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 41

© Copyright 2013, Software Alchemy

Raw UI-binding Techniques

● Create strings of content and/or HTML tags and use setInnerHTML method.

● Create Element objects and perform inserts● Create Widget objects and perform adds● Use raw binding sparingly; better to use the

XML UI-binding config (as seen in other examples)

Page 42: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 42

© Copyright 2013, Software Alchemy

Raw UI-binding Example

Page 43: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 43

© Copyright 2013, Software Alchemy

Raw UI-binding Example

<g:ScrollPanel> <g:HTMLPanel> <!-- Date selection form -->

<g:HTMLPanel ui:field="statsGrid"> <table border='1' cellpadding='5px'> <thead> <tr> <th>Station</th> <th>Category</th> <th><!-- Date goes here --></th> <th>Last 7 days</th> <th>Last 30 days</th> <th>Last 60 days</th> <th>Last year</th> </tr> </thead> <tbody> <!-- View code fills the body --> </tbody> </table> </g:HTMLPanel>

<!-- Reload button form --> </g:HTMLPanel> </g:ScrollPanel>

Page 44: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 44

© Copyright 2013, Software Alchemy

Raw UI-binding Example

public class DailyLoadStatsView extends AbstractPageView { // Skipping lots of other View code private void populateStatsGrid(final LoadStatsModelDTO loadStats) { final Document DOM = Document.get(); List<String> categories = loadStats.getCategories();

// get TABLE element Element gridEl = statsGrid.getElement(); Element tableEl = gridEl.getFirstChildElement();

// put date into "day" header Element theadEl = tableEl.getFirstChildElement(); Element theadTR = theadEl.getFirstChildElement(); Element dateTH = theadTR.getFirstChildElement() .getNextSiblingElement().getNextSiblingElement(); Date reportDate = loadStats.getDailyLoadStats().getDateOfStats(); dateTH.setInnerText(DateUtils.asString(reportDate));

// More code on next slide }}

Page 45: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 45

© Copyright 2013, Software Alchemy

Raw UI-binding Example

private void populateStatsGrid(final LoadStatsModelDTO loadStats) { // More code on previous slide

// build new TBODY tbodyEl = DOM.createTBodyElement(); for (PathwayStation station : PathwayStation.values()) { boolean first = true; boolean odd = true; // Add a row for each category for (String category : categories) { TableRowElement rowEl = DOM.createTRElement(); tbodyEl.appendChild(rowEl); rowEl.addClassName((odd) ? "odd" : "even"); if (first) { // Insert the header on the first category for each station TableCellElement stationTH = DOM.createTHElement(); stationTH.setInnerText(station.name()); stationTH.setRowSpan(categories.size() + 1); rowEl.appendChild(stationTH); first = false; } // Skipping the rest of the code (you get the point) }}

Page 46: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 46

© Copyright 2013, Software Alchemy

XML UI-binding

● Separation of concerns– View.ui.xml:

● Layout of View widgets● Styles of widgets within the View

– View.java:● Inject data into the View's widgets● Handle user events on the View's widgets

– Other CSS files:● Styles of more generic aspects of the UI● Styles of custom widgets

Page 47: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 47

© Copyright 2013, Software Alchemy

XML UI-binding Example

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"><ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:c='urn:import:com.google.gwt.user.cellview.client'> <ui:style> .body { margin: 1em; } .button { margin-top: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; margin-right: 0.25em !important; } </ui:style> <g:DockLayoutPanel unit="EM" styleName="{style.body}"> <g:north size="17"> <!-- Search form --> </g:north> <g:center> <c:DataGrid ui:field='settingsGrid' /> </g:center> </g:DockLayoutPanel></ui:UiBinder>

Widget libraries

Page-specific CSS styles

Fixed-width dock region

Resize-able dock region

Page 48: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 48

© Copyright 2013, Software Alchemy

XML UI-binding Example

<!-- Search form --> <g:north size="17"> <g:DockLayoutPanel unit="EM"> <g:north size="2"> <g:FlowPanel width="100%"> <g:Label stylePrimaryName="alloyLabel">User Group:</g:Label> <g:ListBox ui:field="userGroupList" stylePrimaryName="alloyLabel" width="250" /> </g:FlowPanel> </g:north> <g:center> <g:DockLayoutPanel unit="EM"> <g:north size="2"> <g:FlowPanel width="100%"> <g:Label stylePrimaryName="alloyLabel">Content Set:</g:Label> <g:ListBox ui:field="productList" stylePrimaryName="alloyLabel" width="250" /> </g:FlowPanel> </g:north> <g:center> <g:FlowPanel width="100%"> <g:Label stylePrimaryName="alloyLabel">Collection:</g:Label> <g:ListBox ui:field="collectionList"width="250" multipleSelect="true" /> <!-- Collection selection buttons --> <g:Button ui:field="selectAllBtn">Select All</g:Button> <g:HTML height="1px"> <br /> </g:HTML> <g:Button ui:field="deselectAllBtn">Deselect All</g:Button> </g:FlowPanel> </g:center> </g:DockLayoutPanel> <!-- More View config skipped to fit the slide --> </g:north>

Page 49: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 49

© Copyright 2013, Software Alchemy

XML UI-binding Example

public class FeedLoadSettingsView extends AbstractPageView { interface MyUiBinder extends UiBinder<DockLayoutPanel, FeedLoadSettingsView>{} private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); /* Search criteria fields */ @UiField ListBox userGroupList; @UiField ListBox productList; @UiField ListBox collectionList; @UiField Button selectAllBtn; @UiField Button deselectAllBtn; /* Action Buttons */ @UiField Button searchBtn; @UiField Button saveBtn; @UiField Button resetBtn; /* Feed/Load Setting grid */ @UiField(provided = true) DataGrid<FeedLoadSettingsDTO> settingsGrid; public FeedLoadSettingsView(final FeedLoadSettingsPage page) { super(page); // pre-binding initialization (“provides” the Grid object) initialize(); // createAndBindUi initializes fields initWidget(uiBinder.createAndBindUi(this)); // post-binding initialization userGroupList.getElement().setId(makeFieldId("userGroupList"));// Skipping more code to fit the slide

Page 50: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 50

© Copyright 2013, Software Alchemy

View Styles

● Use stand-alone CSS files for generic classes of styles; usually for custom widgets

● Use UI-binding styles for Page-specific styles● Use programmatic controls to change styles at

run-time– Do this sparingly– Favor changing classes rather than hard-coding

style values– Use GWT's getStyle().setXyzProperty()

as a last resort

Page 51: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 51

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

Styles / CSS

Widgets Panels

RawBinding

UI-Binding

History

Application

Form View

Search

Dashboard

Pages

DEMO #1DEMO #2

ErrorHandling

Security

Gettingstarted

BuildingViews

ExtendingGWT

Talking tothe Server

BuildingPresenters

UsingData Grids

GoogleWeb Toolkit

Building Presenters

Page 52: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 52

© Copyright 2013, Software Alchemy

Presenters == Business Logic

● Application-level logic– Security constraints– Page management and flow

● Page-level logic– Data management & caching– Validation– Server communication

● Widget-level logic

Page 53: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 53

© Copyright 2013, Software Alchemy

Application Entry-Point

● GWT starts up and invokes the onModuleLoad method on the app's entry-point class:

GWT «GWT EntryPoint»Application

«DTO»UserDTO

username : Stringroles : Set<RoleDTO>

onModuleLoad getInitialState

«RPC»ApplicationRPC

«DTO»ApplicationStateDTO

environment : Stringversion : Stringuser : UserDTOproperties : Map<S,S>

Page 54: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 54

© Copyright 2013, Software Alchemy

Application Entry-Point

public class Application implements EntryPoint { private static final ApplicationRPCAsync appRPC = ApplicationRPC.Util.getInstance(); private static ApplicationStateDTO APP_STATE; public final void onModuleLoad() { initializeGUI(); } public final void initializeGUI() { appRPC.getIntialState(new CallbackAdaptor<ApplicationStateDTO>() { public void onSuccess(final ApplicationStateDTO appState) { APP_STATE = appState; propertyMap = appState.getPropertyMap(); buildNewGUI(); navigateToStartLocation(appState.getPlaceToStartCommand()); }; }); } private void buildNewGUI() { homePage = new AlloyHomePage(null); view = new ApplicationView(homePage); // Add the application View directly to the HTML <body> RootLayoutPanel.get().add(view); DOM.getElementById("alloy-loading").getStyle().setDisplay(Display.NONE); }}

Page 55: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 55

© Copyright 2013, Software Alchemy

Error Handling

● RPC calls can throw Java exceptions● The AsyncCallback interface provides the onFailure method hook

● The application can register an exception handler to handle these globally

Page 56: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 56

© Copyright 2013, Software Alchemy

Error Handling

public final void onModuleLoad() { // Save the Singleton instance created by GWT INSTANCE = this;

GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void onUncaughtException(final Throwable error) { if (error != null) { if (error.getCause() != null) { ErrorPopup.center(error.getCause()); } else { ErrorPopup.center(error); } } else { Log.error("Uncaught exception, but error is null"); } } });

initializeGUI(); }

Page 57: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 57

© Copyright 2013, Software Alchemy

Alloy's Error Popup

Page 58: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 58

© Copyright 2013, Software Alchemy

Application Security

● The Application Singleton provides security query methods:

public static boolean isUserInAnyRole(final RoleDTO... roles) { for (RoleDTO role : roles) { if (getCurrentUser().hasRole(role)) { return true; } } return false; }

● Which are then used by Header to filter out specific menus or disable menu items:

private MenuBar createMenuBar() { MenuBar menubar = new MenuBar(); if (Application.isUserInRole(RoleDTO.ROLE_MONITOR)) { menubar.addMenu(createProspectiveMenu()); }

Page 59: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 59

© Copyright 2013, Software Alchemy

Types of Pages

● Alloy uses four basic types of pages:– Dashboards: shows high-level status & system

health– Monitor: view detail status and content flow– Control: modify system properties and other

actions– Search: tools to search on document content

● AbstractPage and AbstractDashboardPage provide basic features of Alloy pages

Page 60: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 60

© Copyright 2013, Software Alchemy

GWT History Management

● Because a GWT app is a single HTML page, user navigation is a little tricky

– The browser's back button, for example, will normally take you back to the page before you entered the GWT app

– GWT provides a hidden <iframe> to manage an internal view transition management

– GWT v2 provides a formal Places/Activities framework, but not used in Alloy

Page 61: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 61

© Copyright 2013, Software Alchemy

Alloy's History Management

GWT1:onValueChange

«Presenter»Page1

2:okToChangePage

3:isDirty

4:changePage

«History Listener»HistoryCmd

Manager

currentPage

8:reset

«Singleton»Application

5:displayPage

6:execute

«Presenter»Page2

7:setState

Page2HistoryChangeHandler

Page 62: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 62

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

Styles / CSS

Widgets Panels

RawBinding

UI-Binding

History

Application

Form View

Search

Dashboard

Pages

DEMO #1DEMO #2

ErrorHandling

Security

SpringIntegration

CallbackAdaptor

DTOs &Transformers

ThinkingAsynchronously

Gettingstarted

BuildingViews

ExtendingGWT

Talking tothe Server

BuildingPresenters

UsingData Grids

GoogleWeb Toolkit

Talking to the Server

Page 63: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 63

© Copyright 2013, Software Alchemy

Spring Integration

● Remember the architecture:

User

Boundary

RPC/async

Entity

DAO

TransformerDTO

RPC/impl

DatabaseDesktop

.................................

Internet Server

GWT Components Spring Components

«cre

ates

»

transformsinto client-side DTO

delegate to CRUD

SQL

reads

server calls GWT's serialization and RPC protocol

over Ajax/HTTP requests

user actions

generates HTML Service

Page 64: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 64

© Copyright 2013, Software Alchemy

Spring Integration

● Based upon a blog post by Chris Lee:GWT-RPC with Spring 2.x

● Uses annotations on POJOs that implement the RPC sync interface:

@GwtRpcEndPointpublic class ProspectiveRPCImpl implements ProspectiveRPC { ... }

● Eliminates web.xml configuration of RPC servlets but must include simple declarations in dispatcher-servlet.xml (see next slide)

Page 65: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 65

© Copyright 2013, Software Alchemy

Spring Integration

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" ...>

<bean class="org.springframework.web.servlet.mvc.anno.AnnoMethodHandlerAdapter" />

<bean id="gwtAnnotationHandlerMapping" class="com.tr.cmg.alloy.ui.server.rpc.GwtAnnotationHandlerMapping" p:suffix=".rpc" p:prefix="/application/" p:order="1" />

<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annno.DefaultAnnoHandlerMapping" p:order="2" />

<bean id="warehouseRPC" class="com.tr.cmg.alloy.ui.server.rpc.warehouse.WarehouseRPCImpl" />

<bean id="delDatamartRPC" class="com.tr.cmg.alloy.ui.server.rpc.datamart.delivery.DelDatamartRPCImpl" />

<!-- Many others... -->

</beans>

Page 66: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 66

© Copyright 2013, Software Alchemy

Callback Adaptor

● The AsyncCallback has two methods:– onSuccess: successful response– onFailure: any HTTP error or service exception

● But usually, you only care about onSuccess● So... create a CallbackAdaptor which

implements the onFailure method– This is a good place to handle any expected

exceptions, such as security exceptions– Let unexpected exceptions be handled by the

Application-level global handler

Page 67: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 67

© Copyright 2013, Software Alchemy

Callback Adaptor

public abstract class CallbackAdaptor<T> implements AsyncCallback<T> { @Override public void onFailure(final Throwable exception) {

if (exception instanceof SessionTimeoutException) { MessageDialog.show(SESSION_TIMEOUT_MSG, GWT_RELOAD_ACTION);

} else if (exception instanceof GWTSecurityException) { SecurityDialog.show((GWTSecurityException) exception, securityAction);

} else if (is404(exception)) { MessageDialog.show(APPLICATION_REBOOT_MSG);

} else if (exception.getMessage().contains(JDBC_CONNECTION_ERROR)) { String message = "A connection to the database could not be established"; String alert = "Database Connection Alert"; ErrorPopup.center(alert, message, exception);

} else { ErrorPopup.center(exception); } }

@Override public abstract void onSuccess(T result);}

Page 68: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 68

© Copyright 2013, Software Alchemy

Callback Decorators

● Do not embed too much extraneous logic in callback methods

● Rather use the Decorator pattern to wrap the primary callback with additional behavior

● Alloy provides several decorators:– ServerWaitDecorator: provides the screen

mask– LoggingDecorator: provides logging of the

result– Lots of other possibilities...

Page 69: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 69

© Copyright 2013, Software Alchemy

Callback Decorator Example

public final class ServerWaitDecorator<T> extends CallbackDecorator<T> { public ServerWaitDecorator(final String maskText, final AsyncCallback<T> wrappedCallback) { super(Preconditions.checkNotNull(wrappedCallback)); // Start the masking process startMask(maskText); } @Override public void onFailure(final Throwable caught) { stopMask(); // Stop the masking process super.onFailure(caught); } @Override public void onSuccess(final T result) { stopMask(); // Stop the masking process super.onSuccess(result); } private void startMask(final String maskText) { ServerWaitPopup.show(maskText); } private void stopMask() { ServerWaitPopup.hide(); }}

Page 70: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 70

© Copyright 2013, Software Alchemy

Callback Decorator Example

// request settings from the ServerAsyncCallback<List<FeedLoadSettingsDTO>> callbackChain = new CallbackAdaptor<List<FeedLoadSettingsDTO>>() { @Override public void onSuccess(final List<FeedLoadSettingsDTO> result) { settings = result; getView(FeedLoadSettingsView.class).setSettingsData(settings); // reset book-keeping data structures resetChanges(); }};// decorate the callbackcallbackChain = new ServerWaitDecorator<List<FeedLoadSettingsDTO>>( "Retrieving settings; please wait...", callbackChain);

// invoke the RPC callprospectiveRPC.retrieveFeedLoadSettings(Arrays.asList(collections), callbackChain);

Page 71: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 71

© Copyright 2013, Software Alchemy

DTOs and Transformers

● You could pass server-side Entity POJOs across the GWT RPC “wire”, but...

– Muddies Entities with GWT-specific interface– Must be in the “GWT shared” package

● If you are using Hibernate and/or JPA, then Entity POJOs are decorated with objects that are not GWT-serializable

● Thus, I recommend separating Entity and DTOs and using transformers at the RPC tier

Page 72: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 72

© Copyright 2013, Software Alchemy

DTOs and Transformers

● Furthermore, there could be real differences between the Entity and DTO classes

– Entity classes might have much more data than the client-side DTO needs

– DTO might have methods that are unique to the needs of the View or Presenter components

● Transformers are basically implementations of the Guava Function<T,F> interface

– Only need to transform individuals– Let GWT's serializer handle lists, sets, maps, etc

Page 73: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 73

© Copyright 2013, Software Alchemy

Transformer Example

@GwtRpcEndPointpublic class ProspectiveRPCImpl implements ProspectiveRPC {

@Autowired private ProspectiveService proService;

private final Function<ProspectiveHealth, ProspectiveHealthDTO> healthTFM = new Function<ProspectiveHealth, ProspectiveHealthDTO>() { @Override public ProspectiveHealthDTO apply(@Nullable ProspectiveHealth input) { return new ProspectiveHealthDTO( input.getRequestProcessorHost(), input.getWorkGeneratorHost(), input.getFinalizerHost(), input.getCleanupHost(), input.getCleanupTimestamp()); } };

public ProspectiveHealthDTO getStationHealth(boolean forceRefresh) { // Delegate to Service ProspectiveHealth entity = proService.getStationHealth(forceRefresh); // Transform to client-side representation return healthTFM.apply(entity); }}

Page 74: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 74

© Copyright 2013, Software Alchemy

Thinking Asynchronously

● Any event-driven environment, such as a user interface, requires the developer to think asynchronously.

● A web UI is doubly so because of the use of Ajax and therefore callbacks to handle the server response.

● What's wrong with this? public void preparePage(final HistoryCommand pendingHistoryCmd) { retrievePathwayConfigFromServer(); retrieveUserGroupsFromServer(); sendSearchRequestToServer(); }

Page 75: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 75

© Copyright 2013, Software Alchemy

Thinking Asynchronously public void preparePage(final HistoryCommand pendingHistoryCmd) { AsyncCallback<PathwayConfigurationDTO> firstCallback = new CallbackAdaptor<PathwayConfigurationDTO>() { @Override public void onSuccess(PathwayConfigurationDTO result) { // store pathway config pathwayConfig = result; // next get the user groups AsyncCallback<List<UserGroupDTO>> secondCallback = new CallbackAdaptor<List<UserGroupDTO>>() { @Override public void onSuccess(List<UserGroupDTO> result) { // store user groups userGroups = result; // finally send the search request AsyncCallback<List<String>> thirdCallback = new CallbackAdaptor<List<String>>() { @Override public void onSuccess(List<String> result) { // send search to the View getView().displaySearch(result); } }; myRPC.sendSearchRequest(thirdCallback); } }; myRPC.retrievePathwayConfig(secondCallback); } }; myRPC.retrievePathwayConfig(firstCallback); }

Page 76: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 76

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

Styles / CSS

Widgets Panels

RawBinding

UI-Binding

History

Application

Form View

Search

Dashboard

Pages

DEMO #1DEMO #2

ErrorHandling

Security

SpringIntegration

CallbackAdaptor

DTOs &Transformers

ThinkingAsynchronously

Widgets &Gadgets

SimpleWidget

ComplexWidget

DialogGadgets

Gettingstarted

BuildingViews

ExtendingGWT

Talking tothe Server

BuildingPresenters

UsingData Grids

GoogleWeb Toolkit

Extending GWT

Page 77: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 77

© Copyright 2013, Software Alchemy

Widgets and Gadgets

● Every non-trivial application will need to have custom widgets and gadgets.

● My definitions:– A widget is a GUI component that is meant to be

embedded within other GUI components or panels, such as a custom drop-down list

– A gadget is a standalone GUI component, such as a dialog box

Page 78: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 78

© Copyright 2013, Software Alchemy

A Simple Widget

● GWT's ListBox widget does not support rich, HTML text in the options, so I build one that does:

/** * An enhancement to the GWT {@link ListBox} widget in which the option text is * rendered as raw HTML. This allows the developer to provide rich text in the * drop-down list. */public class AlloyListBox extends ListBox {

@Override protected void setOptionText(OptionElement opt, String text, Direction dir) { opt.setInnerHTML(text); }

}

Page 79: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 79

© Copyright 2013, Software Alchemy

A Complex Widget – Problem

● GWT's ListBox widget only supports text values. It just mimics the HTML <select> element. For example:

@UiField ListBox userGroupList;

@UiHandler("userGroupList") final void selectUserGroup(final ChangeEvent e) { String group; if (userGroupList.getSelectedIndex() == 0) { group = null; } else { group = userGroupList.getItemText(userGroupList.getSelectedIndex()); } getPage(FeedLoadSettingsPage.class).setSelectedGroup(group); // update Product list List<String> products = null; if (group != null) { products = getPage(FeedLoadSettingsPage.class).getProducts(group); } initProductList(products); }

The getItemText methodreturns a String.

Page 80: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 80

© Copyright 2013, Software Alchemy

A Complex Widget – Solution

● So, I built a drop-down widget that allows the developer to hold any type of object.

● Example now transformed to:

@UiField DropDownList<UserGroupDTO> userGroupList;

@UiHandler("userGroupList") final void selectUserGroup(final ValueChangeEvent<UserGroupDTO> event) { UserGroupDTO group = event.getValue(); getPage(FeedLoadSettingsPage.class).setSelectedGroup(group); // update Product list List<String> products = null; if (group != null) { products = getPage(FeedLoadSettingsPage.class).getProducts(group); } initProductList(products); }

The getValue methodreturns an Object of thethe desired type.

Page 81: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 81

© Copyright 2013, Software Alchemy

DropDownList – Implementation

● DropDownList list management:public class DropDownList<T> extends FocusWidgetimplements HasValueChangeHandlers<T> { private final ListBox selectWidget; private T selectedItem; private Function<T, String> valueFunction; private Function<T, String> textFunction; private final Map<String, T> valueToItemMap = Maps.newHashMap();

public void addItem(T item) { String text = textFunction.apply(item); String value = valueFunction.apply(item); // validate a non-empty value (reserved for "null" item) if (value.isEmpty()) { throw new IllegalArgumentException("The Option '" + text + "' has an empty value."); } // add to <select> element and book-keeping selectWidget.addItem(text, value); valueToItemMap.put(value, item); }}

Favor compositionover inheritance.

Page 82: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 82

© Copyright 2013, Software Alchemy

DropDownList – Implementation

● DropDownList declaration:public class DropDownList<T> extends FocusWidgetimplements HasValueChangeHandlers<T> { private final ListBox selectWidget; private ListBox makeListBox() { ListBox widget = new ListBox(false); widget.addChangeHandler(new ChangeHandler() { public void onChange(ChangeEvent event) { final int selectedIdx = selectWidget.getSelectedIndex(); final String valueText = selectWidget.getValue(selectedIdx); if (Strings.isNullOrEmpty(valueText)) { fireValueChangeEvent(null); } else { fireValueChangeEvent(valueToItemMap.get(valueText)); } } }); return widget; } private void fireValueChangeEvent(final T newValue) { ValueChangeEvent.fireIfNotEqual(this, selectedItem, newValue); selectedItem = newValue; }}

The internal ListBoxchange handler dispatchesto the DropDownListvalue change handlers.

Page 83: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 83

© Copyright 2013, Software Alchemy

Dialog Gadgets

● GWT comes with a PopupPanel component but nothing like a “confirmation dialog.”

Page 84: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 84

© Copyright 2013, Software Alchemy

Confirmation Dialog Example

● The Prospective Dashboard popup menu uses the confirmation dialog like so:

pauseItem = addMenuItem(PAUSE_MENU_LABEL, new Command() { public void execute() { ConfirmationDialog.show(PAUSE_CONFIRMATION_MSG, pauseCmd); } }, Application.CONTROL_ROLES);

● The ConfirmationDialog is a Singleton with static method API to show the dialog.

– It takes a message and a GWT Command object that is invoke if the user clicks the “Ok” button.

Page 85: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 85

© Copyright 2013, Software Alchemy

Confirmation Dialog Example

public final class ConfirmationDialog {

public interface Callback { void onOk(); void onCancel(); } public abstract static class SimpleCallback implements Callback { public void onCancel() { } }

// Singleton pattern private static final ConfirmationDialog INSTANCE = new ConfirmationDialog(); static { RootPanel.get().add(INSTANCE.view); } private ConfirmationDialog() { }

// Attributes private final ConfirmationView view = new ConfirmationView();

// The “show” methods on next slide}

Page 86: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 86

© Copyright 2013, Software Alchemy

Confirmation Dialog Example

public final class ConfirmationDialog {

// More code on previous slide

public static void show(final String title, final String message, final Callback callback) { INSTANCE.view.show(title, message, callback); }

public static void show(final String message, final Callback callback) { show("Please Confirm", message, callback); }

public static void show(final String message, final Command okCommand) { show("Please Confirm", message, new SimpleCallback() { @Override public void onOk() { okCommand.execute(); } }); }}

Page 87: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 87

© Copyright 2013, Software Alchemy

Confirmation Dialog View

public class ConfirmationView extends AlloyPopupPanel { private static final String DIALOG_ID = "confirmationDialog";

interface MyUiBinder extends UiBinder<VerticalPanel, ConfirmationView> { } private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

@UiField Label titleLabel; @UiField HTML messageText; @UiField Button okButton; @UiField Button cancelButton;

ConfirmationView() { super(false, true); setWidget(uiBinder.createAndBindUi(this)); getElement().setId(DIALOG_ID); getElement().getStyle().setWidth(250, Style.Unit.PX); }

final void show(String title, String message, Callback c) { titleLabel.setText(title); messageText.setWordWrap(true); messageText.setHTML(message); callback = c; center(); } // The button handler methods on next slide

Page 88: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 88

© Copyright 2013, Software Alchemy

Confirmation Dialog View

public class ConfirmationView extends AlloyPopupPanel { // More code on previous slide @UiHandler("okButton") final void handleOk(final ClickEvent e) { callback.onOk(); hide(); } @UiHandler("cancelButton") final void handleCancel(final ClickEvent e) { callback.onCancel(); hide(); }}

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <g:VerticalPanel> <g:Label ui:field="titleLabel" styleName="title">Please Confirm</g:Label> <g:HTML ui:field="messageText" styleName="body" /> <g:FlowPanel styleName="buttons"> <g:Button ui:field="okButton">Ok</g:Button> <g:Button ui:field="cancelButton">Cancel</g:Button> </g:FlowPanel> </g:VerticalPanel></ui:UiBinder>

Page 89: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 89

© Copyright 2013, Software Alchemy

Alloy's Popup Panel Class

public abstract class AlloyPopupPanel extends PopupPanel {protected AlloyPopupPanel(final boolean autoHide, final boolean modal) {

super(autoHide, modal); addStyleName("AlloyPopup"); }}

// in Application.css (provides the border and drop-shadow effect)div.AlloyPopup { background-color: #FFFFFF; border-color: #CECECE #BABABA #888888 #CECECE; border-width: 1px; border-style: solid; border-radius: 7px; box-shadow: 10px 10px 5px #75b2d5;}

Page 90: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 90

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

Styles / CSS

Widgets Panels

RawBinding

UI-Binding

History

Application

Form View

Search

Dashboard

Pages

DEMO #1DEMO #2

ErrorHandling

Security

SpringIntegration

CallbackAdaptor

DTOs &Transformers

ThinkingAsynchronously

Widgets &Gadgets

SimpleWidget

ComplexWidget

DialogGadgets

ExtendingGWT

DataGridBasics

Sorting

Paging

Edit GridsGettingstarted

BuildingViews

Talking tothe Server

BuildingPresenters

GoogleWeb Toolkit

UsingData Grids

Using Data Grids

Page 91: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 91

© Copyright 2013, Software Alchemy

The DataGrid Basics

● Two GWT widgets for HTML tables:– CellTable is an ordinary <table> – DataGrid is a resize-able grid where the body

is scrollable● Grids may have headers and footers● Grid columns may support sorting● Grids may support client-side and server-side

paging using the SimplePager widget● Grids can be editable

Page 92: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 92

© Copyright 2013, Software Alchemy

Columncolumns

1..*

DataGrid

TextColumnAlloyDataGrid...

Cellcell

1

NumberCell...

R, C CR

R is the data type of thecontents of each row in the grid.

C is the data type of thecontents of a single column.

C=NumberR, C=StringR

The DataGrid Basics

Page 93: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 93

© Copyright 2013, Software Alchemy

Example Simple Grid

Header

Footer

Page 94: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 94

© Copyright 2013, Software Alchemy

Example Simple Grid

public class MonitorView extends AbstractPageView {

@UiField(provided = true) AlloyDataGrid<MonitorOverviewDTO> overviewGrid;

void populateOverviewGrid(final List<MonitorOverviewDTO> dtos) { overviewGrid.setRowData(dtos); }

private void initialize() { // initialize Overview tab grid overviewGrid = new AlloyDataGrid<MonitorOverviewDTO>("overviewGrid"); overviewGrid.setWidth("75%"); // Date column TextColumn<MonitorOverviewDTO> dateColumn = new TextColumn<...>() { @Override public String getValue(final MonitorOverviewDTO row) { return DateUtils.asString(row.getDate()); } }; dateColumn.setCellStyleNames("columnLeft"); overviewGrid.addColumn(dateColumn, "Date"); overviewGrid.setColumnWidth(dateColumn, DATE_COLUMN_SIZE, Unit.PCT); // ... more column definitions on next slide }

Page 95: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 95

© Copyright 2013, Software Alchemy

Simple Grid – Column w/ Footer

public class MonitorView extends AbstractPageView { private void initialize() { // column definition with Footer Column<MonitorOverviewDTO, Number> workflowsColumn = new Column<MonitorOverviewDTO, Number>( new NumberCell()) { @Override public Number getValue(final MonitorOverviewDTO row) { return row.getWorkflows(); } }; Header<Number> workflowsFooter = new Header<Number>(new NumberCell()) { public Number getValue() { int sum = 0; for (MonitorOverviewDTO row : overviewGrid.getVisibleItems()) { sum += row.getWorkflows(); } return sum; } }; overviewGrid.addColumn(workflowsColumn, new SafeHtmlHeader(SafeHtmlUtils.fromTrustedString("Workflows")), workflowsFooter); overviewGrid.setColumnWidth(workflowsColumn, WORKFLOW_COL_SIZE, Unit.PCT); }

A footer isdefined by aHeader object.Go figure!

Page 96: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 96

© Copyright 2013, Software Alchemy

Grids with Sortable Columns

● Grid columns may support sorting● Grid contains a “sort handler”● Use a “data provider” and sort its list using a

Java Comparator – Need a comparator for the whole grid; must

handle ascending and descending– Need a comparator for each column that

supports sorting

Page 97: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 97

© Copyright 2013, Software Alchemy

Grid Sorting – Screenshot

Ascending icon

Page 98: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 98

© Copyright 2013, Software Alchemy

Grid Sorting – Data Provider

public class MonitorView extends AbstractPageView {

@UiField(provided = true) AlloyDataGrid<MonitorOverviewDTO> overviewGrid; private final ListDataProvider<MonitorOverviewDTO> dataProvider = new ListDataProvider<MonitorOverviewDTO>();

void populateOverviewGrid(final List<MonitorOverviewDTO> dtos) { // sort new list Collections.sort(dtos, gridComparator); // refresh the view dataProvider.setList(dtos); overviewGrid.setVisibleRange(0, dtos.size()); }

private void initialize() { // initialize Overview tab grid overviewGrid = new AlloyDataGrid<MonitorOverviewDTO>("overviewGrid"); overviewGrid.setWidth("75%"); overviewGrid.addColumnSortHandler(contentSortHandler); dataProvider.addDataDisplay(overviewGrid);

Create a data provider.

Attach grid to data provider. Attach a column sort handler.

Sort the data provider.

Page 99: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 99

© Copyright 2013, Software Alchemy

Grid Sorting – Grid Comparator

public class MonitorView extends AbstractPageView {

private static class GridSortComparator implements Comparator<MonitorOverviewDTO> {

// default sort: by Date column ascending private ColumnComparator columnComp = ColumnComparator.DATE; private boolean isAscending = true;

@Override public int compare(MonitorOverviewDTO o1, MonitorOverviewDTO o2) { int ascCompare = columnComp.compare(o1, o2); if (!isAscending) { ascCompare *= -1; // reverse if descending } return ascCompare; } }

private final GridSortComparator gridComparator = new GridSortComparator();

Page 100: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 100

© Copyright 2013, Software Alchemy

Grid Sorting – Column Comparators as an Enum

public class MonitorView extends AbstractPageView {

enum ColumnComparator implements Comparator<MonitorOverviewDTO> { DATE() { public int compare(MonitorOverviewDTO o1, MonitorOverviewDTO o2) { return o1.getDate().compareTo(o2.getDate()); } }, WORKFLOWS() { public int compare(MonitorOverviewDTO o1, MonitorOverviewDTO o2) { return o1.getWorkflows() - o2.getWorkflows(); } }, DOCUMENTS() { public int compare(MonitorOverviewDTO o1, MonitorOverviewDTO o2) { return (int) (o1.getDocuments() - o2.getDocuments()); } } }

Page 101: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 101

© Copyright 2013, Software Alchemy

Grid Sorting – Sort Handler

public class MonitorView extends AbstractPageView {

private final ColumnSortEvent.Handler contentSortHandler = new Handler() { @Override public void onColumnSort(final ColumnSortEvent event) { // determine which column is being sorted if (event.getColumn() == dateColumn) { gridComparator.columnComp = ColumnComparator.DATE; } else if (event.getColumn() == workflowsColumn) { gridComparator.columnComp = ColumnComparator.WORKFLOWS; } else if (event.getColumn() == documentsColumn) { gridComparator.columnComp = ColumnComparator.DOCUMENTS; } else { Log.error("Unknown column to sort on: " + event.getColumn()); } gridComparator.isAscending = event.isSortAscending(); // sort the data provider List<MonitorOverviewDTO> gridData = dataProvider.getList(); Collections.sort(gridData, gridComparator); // refresh the grid via the data provider dataProvider.refresh(); } };

Page 102: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 102

© Copyright 2013, Software Alchemy

Grid Sorting – Declare Columns

public class MonitorView extends AbstractPageView {

private TextColumn<MonitorOverviewDTO> dateColumn; private Column<MonitorOverviewDTO, Number> workflowsColumn; private Column<MonitorOverviewDTO, Number> documentsColumn;

private void initialize() { // Date column dateColumn = new TextColumn<MonitorOverviewDTO>() { @Override public String getValue(final MonitorOverviewDTO row) { return DateUtils.asString(row.getDate()); } }; dateColumn.setSortable(true); dateColumn.setCellStyleNames("columnLeft"); overviewGrid.addColumn(dateColumn, "Date"); overviewGrid.setColumnWidth(dateColumn, FIRST_COL_SIZE, Unit.PCT);

Declare column is sortable.

Page 103: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 103

© Copyright 2013, Software Alchemy

Grid Paging

● Paging requires a data provider● Paging requires an additional widget: such as a SimplePager

● Paging might be client-side or server-side– Server-side paging uses an

AsyncDataProvider which performs incremental RPC calls when changing pages

– Client-side paging can just use a simple ListDataProvider

Page 104: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 104

© Copyright 2013, Software Alchemy

Example Paged Grid

Simple paging controls.

Page 105: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 105

© Copyright 2013, Software Alchemy

Example Paged Grid

public class LinkAnalysisView extends AbstractPageView {

@UiField(provided = true) DataGrid<RrdResultDTO> erdNovusLinksGrid; @UiField SimplePager erdGridPager;

private AsyncDataProvider<RrdResultDTO> linksDataProvider;

public LinkAnalysisView(AbstractPage pageIn) { super(pageIn); erdNovusLinksGrid = new AlloyDataGrid<RrdResultDTO>(makeFieldId("grid")); erdNovusLinksGrid.setWidth("75%"); erdGridPager.setDisplay(erdNovusLinksGrid); erdGridPager.setPageSize(PAGE_SIZE); }

Page 106: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 106

© Copyright 2013, Software Alchemy

Example Paged Grid

public class LinkAnalysisView extends AbstractPageView {

void erdSetRrdResults(final QueryResultDTO<RrdResultDTO> rrdResults) { erdNovusLinksGrid.setRowCount(rrdResults.getTotalCount(), true); erdNovusLinksGrid.setVisibleRange(0, PAGE_SIZE); if (rrdResults.getItems().size() == 0) { erdNovusLinksGrid.setRowData(rrdResults.getItems()); } else { // create the data provider that performs server-side paging linksDataProvider = new RrdSearchDataProvider(erdGuidField.getText()){ @Override protected void sendMatchRequest( String guid, QueryRequestDTO queryInfo, AsyncCallback<QueryResultDTO<RrdResultDTO>> callback) { LinkAnalysisRPC.Util.getInstance().matchErdGUID( guid, queryInfo, callback); } }; // connect the data provider to the grid linksDataProvider.addDataDisplay(erdNovusLinksGrid); } }

Custom data provider(see next slide)

The RPC call

Page 107: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 107

© Copyright 2013, Software Alchemy

Custom Async Data Provider

public abstract class RrdSearchDataProvider extends AsyncDataProvider<RrdResultDTO> { private final String guid; public RrdSearchDataProvider(final String guid) { this.guid = guid; } protected void onRangeChanged(final HasData<RrdResultDTO> display) { // use the new grid Range to request a page from the server final Range range = display.getVisibleRange(); // build the paged request final QueryRequestDTO queryInfo = new QueryRequestDTO(range.getStart(), range.getLength()); // build the callback chain AsyncCallback<QueryResultDTO<RrdResultDTO>> callbackChain = new CallbackAdaptor<QueryResultDTO<RrdResultDTO>>() { public final void onSuccess(final QueryResultDTO<RrdResultDTO> results) { // when the server response comes, update the grid updateRowData(range.getStart(), results.getItems()); // set the total row count display.setRowCount(results.getTotalCount()); } }; // send the request to the server sendMatchRequest(guid, queryInfo, callbackChain); } protected abstract void sendMatchRequest( String guid, QueryRequestDTO queryInfo, AsyncCallback<QueryResultDTO<RrdResultDTO>> callback);}

Page 108: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 108

© Copyright 2013, Software Alchemy

Editable Grids

● Grid cells (and header) can contain edit widgets, such as text fields, check boxes, and list boxes

● Use a FieldUpdater component on the column definition

● The application must manage how the changes are sent to the server; it's not automatic

Page 109: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 109

© Copyright 2013, Software Alchemy

Example Editable Grid

Page save/reset buttons

Column checkboxHighlights when changed.

Page 110: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 110

© Copyright 2013, Software Alchemy

Example Editable Grid

public class FeedLoadSettingsView extends AbstractPageView {

@UiField(provided = true) AlloyDataGrid<FeedLoadSettingsDTO> settingsGrid;

private void initialize() { settingsGrid = new AlloyDataGrid<FeedLoadSettingsDTO>("settingsGrid"); // Feed? Column final FieldUpdater<FeedLoadSettingsDTO, Boolean> feedUpdater = new FieldUpdater<FeedLoadSettingsDTO, Boolean>() { @Override public void update(final int index, final FeedLoadSettingsDTO row, final Boolean value) { InputElement checkboxEl = findCheckboxCellField(index, FEED_CELL_INDEX); checkboxEl.setChecked(value.booleanValue()); row.updateFeeding(value); if (row.isFeedingDirty()) { checkboxEl.addClassName(HtmlUtils.FIELD_CHANGED_CLASS); } else { checkboxEl.removeClassName(HtmlUtils.FIELD_CHANGED_CLASS); } evaluateSettingsStatus(); } };

Store the change

Enable/disable the Save& Reset buttons.

Page 111: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 111

© Copyright 2013, Software Alchemy

Example Editable Grid

public class FeedLoadSettingsView extends AbstractPageView {

private final CheckboxCell feedCheckboxCell = new SecureCheckboxCell( RoleDTO.ROLE_SUPER_CONTROL);

private void initialize() { settingsGrid = new AlloyDataGrid<FeedLoadSettingsDTO>("settingsGrid"); // Feed? Column (more) final FieldUpdater<FeedLoadSettingsDTO, Boolean> feedUpdater = ...; Column<FeedLoadSettingsDTO, Boolean> feedCol = new Column<FeedLoadSettingsDTO, Boolean>(feedCheckboxCell) { @Override public Boolean getValue(final FeedLoadSettingsDTO row) { return row.isFeeding(); } }; feedCol.setFieldUpdater(feedUpdater); feedCol.setCellStyleNames("columnCenter"); settingsGrid.addColumn(feedCol, feedHdr); settingsGrid.setColumnWidth(feedCol, FEED_COL_SIZE, Unit.PCT);

Custom cell widget

Page 112: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 112

© Copyright 2013, Software Alchemy

Custom Editable Cell Widget

public class SecureCheckboxCell extends CheckboxCell {

private static final String ENABLED_AND_CHECKED = "<input type=\"checkbox\" tabindex=\"-1\" checked />"; private static final String ENABLED_AND_UNCHECKED = "<input type=\"checkbox\" tabindex=\"-1\" />"; private static final String DISABLED_AND_CHECKED = "<input type=\"checkbox\" tabindex=\"-1\" checked disabled=\"disabled\" />"; private static final String DISABLED_AND_UNCHECKED = "<input type=\"checkbox\" tabindex=\"-1\" disabled=\"disabled\" />";

private final RoleDTO[] enabledForRoles;

public SecureCheckboxCell(final RoleDTO... enabledForRolesIn) { super(false, false); enabledForRoles = enabledForRolesIn; }

// continued on next slide

Page 113: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 113

© Copyright 2013, Software Alchemy

Custom Editable Cell Widget

public class SecureCheckboxCell extends CheckboxCell { // continued from previous slide public void render(final Context context, final Boolean value, final SafeHtmlBuilder sb) { // Get the view data. Object key = context.getKey(); Boolean viewData = getViewData(key); if (viewData != null && viewData.equals(value)) { clearViewData(key); viewData = null; } if (value != null && ((viewData != null) ? viewData : value)) { if (Application.isUserInAnyRole(enabledForRoles)) { sb.append(SafeHtmlUtils.fromSafeConstant(ENABLED_AND_CHECKED)); } else { sb.append(SafeHtmlUtils.fromSafeConstant(DISABLED_AND_CHECKED)); } } else { if (Application.isUserInAnyRole(enabledForRoles)) { sb.append(SafeHtmlUtils.fromSafeConstant(ENABLED_AND_UNCHECKED)); } else { sb.append(SafeHtmlUtils.fromSafeConstant(DISABLED_AND_UNCHECKED)); } } }}

Page 114: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 114

© Copyright 2013, Software Alchemy

Config

Single-PageModel

Like Java RMI

MVPRPC ...but asynchronous

ModelView

ModelPresenter

Styles / CSS

Widgets Panels

RawBinding

UI-Binding

History

Application

Form View

Search

Dashboard

Pages

DEMO #1DEMO #2

ErrorHandling

Security

SpringIntegration

CallbackAdaptor

DTOs &Transformers

ThinkingAsynchronously

Widgets &Gadgets

SimpleWidget

ComplexWidget

DialogGadgets

ExtendingGWT

Gettingstarted

BuildingViews

Talking tothe Server

BuildingPresenters

GoogleWeb Toolkit

DataGridBasics

Sorting

Paging

Edit Grids

UsingData Grids

Q & A

Page 115: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 115

© Copyright 2013, Software Alchemy

User Presenter

RPC/async

DTO

reads

server calls

user actions

generates HTML View

signals events

sends data

View.ui.xml

....

..

.....

....

View.java Presenter.java

styles.css

....

..

.....

....

DTO.java 3 RPC types

LAST WORD:GWT is not Light-Weight

● As with any Web app, GWT requires a fair amount of infrastructure:

Page 116: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 116

© Copyright 2013, Software Alchemy

5 Great Topics (not covered)

1) Testing: Unit (eg, jUnit or TestNG) and Integration (eg, Selenium)

2) GWT history management using Places/Activities

3) Third-party libraries, some good (SmartGWT) and some great (GXT)

4) Cross-site scripting and GWT's SafeHtml

5) GWT's RequestFactory for simplified (yeah right) data access

Page 117: Google Web Toolkit: a case study

RJUG : 12-Mar-2012

Bryan Basham – The Google Web Toolkit Slide 117

© Copyright 2013, Software Alchemy

Resources

● Wikipedia

● Official GWT Home

● GWT Widget Gallery

● GWT API javadocs