More Related Content Similar to Google Web Toolkit: a case study (20) Google Web Toolkit: a case study1. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
The Google Web Toolkit
Getting
started
Using Building
Data Grids Views
Google
Web Toolkit
Extending Building
GWT Presenters
Talking to
the Server
Bryan Basham
Software Alchemy
basham47@gmail.com
http://www.linkedin.com/in/SoftwareAlchemist
Bryan Basham – The Google Web Toolkit Slide 1
2. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Getting Started...
View
Presenter
Model Like Java RMI
MVP ...but asynchronous
RPC
Single-Page
Model
Config
Getting
started
DEMO #1
Using Building
Data Grids Views
Google
Web Toolkit
Extending Building
GWT Presenters
Talking to
the Server
Bryan Basham – The Google Web Toolkit Slide 2
3. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 3
4. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Demo: “Hello World”
Bryan Basham – The Google Web Toolkit Slide 4
5. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Typical GWT Architecture
Desktop Internet Server Database
User
...........
...........
...........
Client-side Server-side
Components Components
& DTOs & Entities
Bryan Basham – The Google Web Toolkit Slide 5
6. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Typical GWT Architecture
Desktop Internet Server Database
User
...........
...........
...........
use
gen
r ac
e rate
tion
sH
GWT's serialization and RPC protocol
s
s
all Other
TM
rc RPC over Ajax/HTTP requests RPC
rve Server-side
L
se /async /impl
Components
Boundary reads & Entities
transfer objects (both directions)
DTO
GWT Components
Bryan Basham – The Google Web Toolkit Slide 6
7. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Typical GWT Architecture
Desktop Internet Server Database
User
...........
...........
...........
use
gen
L
SQ
r ac
e rate
tion
sH
GWT's serialization and RPC protocol delegate to CRUD
s
s
all
TM
rc RPC over Ajax/HTTP requests RPC Service DAO
rve
L
se /async /impl
s»
a te
re
Boundary reads
«c
transfer objects (both directions) ?
DTO Entity
GWT Components Spring Components
Bryan Basham – The Google Web Toolkit Slide 7
8. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Typical GWT Architecture
Desktop Internet Server Database
User
...........
...........
...........
use
gen
L
SQ
r ac
e rate
tion
sH
GWT's serialization and RPC protocol delegate to CRUD
s
ll s
TM
ca RPC over Ajax/HTTP requests RPC Service DAO
rv er
L
se /async /impl
s»
a te
re
Boundary reads
«c
into client-side DTO transforms
DTO Transformer Entity
GWT Components Spring Components
Bryan Basham – The Google Web Toolkit Slide 8
9. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 9
10. RJUG : 12-Mar-2012 © 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.
Bryan Basham – The Google Web Toolkit Slide 10
11. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 11
12. RJUG : 12-Mar-2012 © 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.
Bryan Basham – The Google Web Toolkit Slide 12
13. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Model-View-Presenter
● Similar to the popular Model-View-Controller
pattern, but cleaner separation:
ls
e r cal RPC
serv /async
user actions signals events
generates HTML View sends data Presenter reads
User
DTO
– 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
Bryan Basham – The Google Web Toolkit Slide 13
14. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 14
15. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 15
16. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 16
17. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 17
18. RJUG : 12-Mar-2012 © 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);
}
}
Bryan Basham – The Google Web Toolkit Slide 18
19. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 19
20. RJUG : 12-Mar-2012 © 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);
}
});
}
}
Bryan Basham – The Google Web Toolkit Slide 20
21. RJUG : 12-Mar-2012 © 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?
Bryan Basham – The Google Web Toolkit Slide 21
22. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 22
23. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
RPC (Architecture Diagram)
The Async interface is based upon the
Sync interface but with an additional
Callback<ReturnType> parameter.
«interface» «interface»
MyRPCAsync MyRPC
This class implementation is provided
by GWT which contains boiler-plate code
to make Ajax requests.
server calls GWT's serialization and RPC protocol delegates to
Boundary «class» over Ajax/HTTP requests GWT RPC «class»
MyRPCAsync_ servlet MyRPCImpl
impl
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
Bryan Basham – The Google Web Toolkit Slide 23
24. RJUG : 12-Mar-2012 © 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 GreetingServiceImpl
extends 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;
}
}
Bryan Basham – The Google Web Toolkit Slide 24
25. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 25
26. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
RPC (Interaction Diagram)
Boundary «class» GWT RPC GWT RPC «class»
MyRPCAsync_ invoker servlet MyRPCImpl
impl
sayHello(name, callback)
payload[sayHello|Fred]
sayHello(name)
name:String
“Fred” name:String
“Fred”
callback:
AsyncCallback<String>
return
payload[Hello Fred]
result:String
onSuccess(result) “Hello Fred”
result:String
“Hello Fred”
Bryan Basham – The Google Web Toolkit Slide 26
27. RJUG : 12-Mar-2012 © 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 the
primary config file.
Bryan Basham – The Google Web Toolkit Slide 27
28. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 28
29. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 29
30. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Building Views
View
Presenter
Model Like Java RMI
MVP ...but asynchronous
RPC
Single-Page
Model
Config
Getting Panels
Widgets
started
DEMO #1 Raw
DEMO #2
Binding
Using Building
Data Grids Views
UI-Binding
Styles / CSS
Google
Web Toolkit
Extending Building
GWT Presenters
Talking to
the Server
Bryan Basham – The Google Web Toolkit Slide 30
31. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
DEMO #2
● Alloy is a Monitor & Control app on a document
processing pathway:
....
..
.....
....
document1 Datamart1
User
....
..
..... Prospective Warehouse
....
document2
Datamart2
....
..
.....
User
....
document3
● Also: search, reports, admin tools
Bryan Basham – The Google Web Toolkit Slide 31
32. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 32
33. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
DEMO #2: Dashboards
Header
Bread-
crumb
Pages
Deck
Footer
Bryan Basham – The Google Web Toolkit Slide 33
34. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
DEMO #2: Monitor page
Bryan Basham – The Google Web Toolkit Slide 34
35. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
DEMO #2: Control page
Bryan Basham – The Google Web Toolkit Slide 35
36. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 36
37. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 37
38. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 38
39. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Alloy's Core GUI Architecture
● Application contains many Pages with one
View each: Extending this class forces each page
«GWT Widget»
view to be resize-able. It is the developer's
responsibility to decide how the page's
Resize
layout is constructed to support resizing. Composite
1..* «Presenter» 1 «View»
«GWT EntryPoint»
Abstract Abstract
Application pages view
Page PageView
... ...
«Presenter» «View»
Prospective Prospective
FeedLoadPage FeedLoadView
Bryan Basham – The Google Web Toolkit Slide 39
40. RJUG : 12-Mar-2012 © 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)
Bryan Basham – The Google Web Toolkit Slide 40
41. RJUG : 12-Mar-2012 © 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)
Bryan Basham – The Google Web Toolkit Slide 41
42. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Raw UI-binding Example
Bryan Basham – The Google Web Toolkit Slide 42
43. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 43
44. RJUG : 12-Mar-2012 © 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
}
}
Bryan Basham – The Google Web Toolkit Slide 44
45. RJUG : 12-Mar-2012 © 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)
}
}
Bryan Basham – The Google Web Toolkit Slide 45
46. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 46
47. RJUG : 12-Mar-2012 © 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'> Widget libraries
<ui:style>
.body {
margin: 1em;
}
.button {
margin-top: 0 !important; Page-specific CSS styles
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 --> Fixed-width dock region
</g:north>
<g:center>
<c:DataGrid ui:field='settingsGrid' /> Resize-able dock region
</g:center>
</g:DockLayoutPanel>
</ui:UiBinder>
Bryan Basham – The Google Web Toolkit Slide 47
48. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 48
49. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 49
50. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 50
51. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Building Presenters
View
Presenter
Model Like Java RMI
MVP ...but asynchronous
RPC
Single-Page
Model
Config
Getting Panels
Widgets
started
DEMO #1 Raw
DEMO #2
Binding
Using Building
Data Grids Views
UI-Binding
Styles / CSS
Google
Web Toolkit
Application
Error
Extending Building Handling
GWT Presenters Security
Dashboard
Talking to History
the Server Pages
Search
View
Form
Bryan Basham – The Google Web Toolkit Slide 51
52. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 52
53. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Application Entry-Point
● GWT starts up and invokes the onModuleLoad
method on the app's entry-point class:
«GWT EntryPoint» «RPC»
GWT onModuleLoad Application getInitialState ApplicationRPC
«DTO» «DTO»
Application UserDTO
StateDTO
username : String
environment : String roles : Set<RoleDTO>
version : String
user : UserDTO
properties : Map<S,S>
Bryan Basham – The Google Web Toolkit Slide 53
54. RJUG : 12-Mar-2012 © 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);
}
}
Bryan Basham – The Google Web Toolkit Slide 54
55. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 55
56. RJUG : 12-Mar-2012 © 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();
}
Bryan Basham – The Google Web Toolkit Slide 56
57. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Alloy's Error Popup
Bryan Basham – The Google Web Toolkit Slide 57
58. RJUG : 12-Mar-2012 © 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());
}
Bryan Basham – The Google Web Toolkit Slide 58
59. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 59
60. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 60
61. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Alloy's History Management
«History Listener»
GWT 1:onValueChange
HistoryCmd
Manager
e
ag
eP ge
ang Pa
h ge
T oC an
k h
2:o 4:c
5:displayPage
«Singleton» «Presenter»
Application Page2
7:setState
3:isDirty
6:execute
8:reset
currentPage
«Presenter» Page2History
Page1 ChangeHandler
Bryan Basham – The Google Web Toolkit Slide 61
62. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Talking to the Server
View
Presenter
Model Like Java RMI
MVP ...but asynchronous
RPC
Single-Page
Model
Config
Getting Panels
Widgets
started
DEMO #1 Raw
DEMO #2
Binding
Using Building
Data Grids Views
UI-Binding
Styles / CSS
Google
Web Toolkit
Application
Error
Extending Building Handling
GWT Presenters Security
Dashboard
Talking to History
the Server Pages
Thinking Search
Asynchronously Spring
Integration View
Form
DTOs & Callback
Transformers Adaptor
Bryan Basham – The Google Web Toolkit Slide 62
63. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Spring Integration
● Remember the architecture:
Desktop Internet Server Database
User
...........
...........
...........
use
gen
L
SQ
r ac
e rate
tion
sH
GWT's serialization and RPC protocol delegate to CRUD
s
ll s
TM
ca RPC over Ajax/HTTP requests RPC Service DAO
rv er
L
se /async /impl
s»
a te
re
Boundary reads
«c
into client-side DTO transforms
DTO Transformer Entity
GWT Components Spring Components
Bryan Basham – The Google Web Toolkit Slide 63
64. RJUG : 12-Mar-2012 © 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:
@GwtRpcEndPoint
public class ProspectiveRPCImpl implements ProspectiveRPC { ... }
● Eliminates web.xml configuration of RPC
servlets but must include simple declarations in
dispatcher-servlet.xml (see next slide)
Bryan Basham – The Google Web Toolkit Slide 64
65. RJUG : 12-Mar-2012 © 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>
Bryan Basham – The Google Web Toolkit Slide 65
66. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 66
67. RJUG : 12-Mar-2012 © 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);
}
Bryan Basham – The Google Web Toolkit Slide 67
68. RJUG : 12-Mar-2012 © 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...
Bryan Basham – The Google Web Toolkit Slide 68
69. RJUG : 12-Mar-2012 © 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();
}
}
Bryan Basham – The Google Web Toolkit Slide 69
70. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Callback Decorator Example
// request settings from the Server
AsyncCallback<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 callback
callbackChain = new ServerWaitDecorator<List<FeedLoadSettingsDTO>>(
"Retrieving settings; please wait...", callbackChain);
// invoke the RPC call
prospectiveRPC.retrieveFeedLoadSettings(Arrays.asList(collections),
callbackChain);
Bryan Basham – The Google Web Toolkit Slide 70
71. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 71
72. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 72
73. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Transformer Example
@GwtRpcEndPoint
public 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);
}
}
Bryan Basham – The Google Web Toolkit Slide 73
74. RJUG : 12-Mar-2012 © 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();
}
Bryan Basham – The Google Web Toolkit Slide 74
75. RJUG : 12-Mar-2012 © 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);
}
Bryan Basham – The Google Web Toolkit Slide 75
76. RJUG : 12-Mar-2012 © Copyright 2013, Software Alchemy
Extending GWT
View
Presenter
Model Like Java RMI
MVP ...but asynchronous
RPC
Single-Page
Model
Config
Getting Panels
Widgets
started
DEMO #1 Raw
DEMO #2
Binding
Using Building
Data Grids Views
UI-Binding
Styles / CSS
Google
Web Toolkit
Dialog
Application
Gadgets
Error
Extending Building Handling
Complex
Widget GWT Presenters Security
Simple Dashboard
Widget Talking to History
Widgets & the Server Pages
Gadgets Thinking Search
Asynchronously Spring
Integration View
Form
DTOs & Callback
Transformers Adaptor
Bryan Basham – The Google Web Toolkit Slide 76
77. RJUG : 12-Mar-2012 © 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
Bryan Basham – The Google Web Toolkit Slide 77
78. RJUG : 12-Mar-2012 © 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);
}
}
Bryan Basham – The Google Web Toolkit Slide 78