8/9/2019 Prague GWT
1/39
Real GWT ApplicationsJeff DwyerAuthor: Pro Web 2.0 Applications with GWT
Founder: ToCollege.net & MyHippocampus.com
Currently: (rails)
8/9/2019 Prague GWT
2/39
Today
What and Why GWT?
Demo ToCollege.net
Real Apps Integration Patterns
Hibernate Security
Gears
8/9/2019 Prague GWT
3/39
Goal
Rich web experiences. Speed and responsiveness of desktop.
Benefits of the web.
8/9/2019 Prague GWT
4/39
Landscape
JavaScript+ Server RichClient
YUI
Dojo
Rico
Script.aclo.us
jMaki
Flex
Silverlight
Echo
Lazlo
JSF
GWT
8/9/2019 Prague GWT
5/39
Why GWT
GWT Brings Java Software DevelopmentBest Practices to AJAX HTML
GWT is DHTML + JavaScript JavaScript doesnt scale... with increased
development staff.
Best Practices: i18n, History, ImageBundle
8/9/2019 Prague GWT
6/39
Features
Easy RPC: Single domain for client & server
History Mechanism Extremely Cacheability
Browser Compatibility
Real Debugging & JUnit & I18N
8/9/2019 Prague GWT
7/39
Benefits & Joys
One Domain Model
Static Typing
IDE Refactoring, Code Completion FindBugs, PMD, Checkstyle, Metrics
Hosted Mode. Real Debugging. Maven
8/9/2019 Prague GWT
8/39
Tradeoffs? Code Size? Think HttpRequests > size
Cache Forever Debugable w/ Firebug? Use pretty mode or real debugger
Script.aculo.us? JSNI is great, or whip together yourself.
SEO...
8/9/2019 Prague GWT
9/39
How it Works
Swing + CSS
Java to JavaScript compiler Compressed and Obfuscated by default
Include in HTML with and
Locale Specific Compilation
8/9/2019 Prague GWT
10/39
Demo
www.ToCollege.net http://code.google.com/p/tocollege-net/
http://code.google.com/p/tocollege-net/http://code.google.com/p/tocollege-net/http://code.google.com/p/tocollege-net/8/9/2019 Prague GWT
11/39
ToCollege.net Stack
MySQL / Hibernate
Compass
Spring MVC 2.5 Acegi & OpenID
SiteMesh & FreeMarker GWT 1.5 Integration SEO, Security
Also: Facebook, Maven
8/9/2019 Prague GWT
12/39
VerticalLabel
8/9/2019 Prague GWT
13/39
public class VerticalLabel extends Composite {public interface LetterImages extends ImageBundle {
AbstractImagePrototype A();
AbstractImagePrototype B();AbstractImagePrototype sY();
AbstractImagePrototype sZ();
AbstractImagePrototype SPACE();
}
private FlowPanel mainPanel;
private HashMap allImages;private static final LetterImages images =
(LetterImages) GWT.create(LetterImages.class);
public VerticalLabel(String text) {
if (allImages == null) {
createMap();
}
mainPanel = new FlowPanel();
mainPanel.setStylePrimaryName("vertical-label");
setText(text);
initWidget(mainPanel);
}private void setText(String text) {
mainPanel.clear();
for (int i = text.length() - 1; i >= 0; i--) {
char c = text.charAt(i);
mainPanel.add(getImage(c));
}
}
}
VerticalLabel.java
8/9/2019 Prague GWT
14/39
The Joy of IE6 w/ Apologies to Joel Webber
Interface:
public abstract boolean compare(Element e1, Element e2);Standard:
public native boolean compare(Element e1, Element e2) /*-{return (e1 == e2);
}-*/;IE6:
public native boolean compare(Element e1, Element e2) /*-{if (!e1 && !e2)
return true;else if (!e1 || !e2)
return false;return (e1.uniqueID == e2.uniqueID);
}-*/;
Deferred Binding
8/9/2019 Prague GWT
15/39
public class ForumApp extends GWTApp implements
HistoryListener {
public ForumApp(int pageID) {if (initToken.length() > 0) {
onHistoryChanged(initToken);
}History.addHistoryListener(this);
}
// #School~486~20
// #UserForumPost~12~0// #UserForumPost~12
public void onHistoryChanged(String historyToken) {ForumCommand fc = new ForumCommand();
String[] tok = historyToken.split(ForumTopic.SEP);
fc.setId(Long.parseLong(tok[1]));if (tok.length == 3) {
fc.setStart(Integer.parseInt(tok[2]));
}
fc.setType(tok[0]);load(fc);
}
}
History(Back Button)
http://www.tocollege.net/forums.html#SchoolForumPost~112
http://www.tocollege.net/forums.html#http://www.tocollege.net/forums.html#http://www.tocollege.net/forums.html#http://www.tocollege.net/forums.html#8/9/2019 Prague GWT
16/39
SampleApp.java1.5 Source
JREEmulation
GWTCompiler
Gecko(Fr)
Gecko(En)
IE (En)
IE (Fr)function gGd(a){return (this==null?null:this)===(a==null?null:a)}function hGd(){return gmb}function iGd(){return this.$H||(this.$H=++Cvc)}
... (...)
8/9/2019 Prague GWT
17/39
SampleApp.java1.5 Source
JREEmulation
GWTCompiler
Gecko Gecko
IE (En)
IE (Fr)function gGd(a){return (this==null?null:this)===(a==null?null:a)}function hGd(){return gmb}function iGd(){return this.$H||(this.$H=++Cvc)}
IE
... (...)
Spring MVC, PHP, Rails, etc
?
8/9/2019 Prague GWT
18/39
SampleApp.java1.5 Source
JREEmulation
GWTCompiler
Gecko Gecko
IE (En)
IE (Fr)function gGd(a){return (this==null?null:this)===(a==null?null:a)}function hGd(){return gmb}function iGd(){return this.$H||(this.$H=++Cvc)}
IE
... (...)
SpringMVCPHP
Rails
8/9/2019 Prague GWT
19/39
Simple GWT Integration
protectedvoid loadError(Exception e) {
VerticalPanel panel = new VerticalPanel();
panel.add(new Label("Error"));
panel.add(new Label(e.getMessage()));
RootPanel.get(gwt-preload).setVisible(false);
RootPanel.get(gwt-slot).add(panel);}
8/9/2019 Prague GWT
20/39
Real Applications
Integration
JavaScript & FreeMarker Macros
Hibernate Command Pattern
Security XSRF & Command Pattern
Gears
8/9/2019 Prague GWT
21/39
Integration
div id == Java Code What about multiple component talking to
each other?
What about re-use?
This is where people screw up. Manymodules, etc.
Solution? Leverage JavaScript.
8/9/2019 Prague GWT
22/39
Spring MVC Integration
Macros are your friend
Component-ization GWT-RPC
SEO w/ Bootstrapping &
8/9/2019 Prague GWT
23/39
Using GWT: Macros
8/9/2019 Prague GWT
24/39
Using GWT: JavaScript
FreeMarker
GWTDispatcher
VerticalImageApp ToCollegeApp ImageBundleApp
Vars['widgetCount'] = "2"
Vars['widget_1'] = "ImageBundle"
Vars['widget_2'] = "VerticalImage"Vars['widget_2_text'] = "Make Me Vertical!"
8/9/2019 Prague GWT
25/39
Hiberate: Architecture
8/9/2019 Prague GWT
26/39
Hibernate4GWT
User
Application
School
Hib4GWTProxy
1..1
GWT Client
User
Application
School
Reviews (Lazy)
1..1
ServerGWTRPC
&
Hib4GWT
8/9/2019 Prague GWT
27/39
Hibernate w/GWTHibernateFilter
User
Application
School
Null
1..1
GWT Client
User
Application
School
Reviews (Lazy)
1..1
ServerGWTRPC
&GWTHibernateFilter
8/9/2019 Prague GWT
28/39
Pro & Cons
Hibernate4GWT GWTHibernateFilter
Pros Can use save(Object) No change to objects
ConsOverhead.Must implementHibernate4GWT
Cant simply call save()Lazy collections != null
8/9/2019 Prague GWT
29/39
RPC Servicepublicinterface GWTSchoolService extends RemoteService {
SiteCommand executeAndSaveCommand(SiteCommand comm)
throws SiteException;
List getAllSchools() throws SiteException;
PostsList getForum(String tagname, int start, int max)
throws SiteException;
School getSchoolDetails(String schoolName);
List getSchoolsMatching(String match) throws SiteException;
List matchProcessType(String queryString)
throws SiteException;
}
8/9/2019 Prague GWT
30/39
Get Idempotent
SOFE
Spring Security
ServerPostsList getForum(String tagname, int start, int max)
throws SiteException;
School getSchoolDetails(String schoolName);
8/9/2019 Prague GWT
31/39
PostSOFE
Spring Security
Server
SiteCommand executeAndSaveCommand(SiteCommand comm)
throws SiteException;
Command
XSRF Protection
8/9/2019 Prague GWT
32/39
Command Pattern
Hibernate Integration
Authorization XSRF
Gears / Offline
8/9/2019 Prague GWT
33/39
save(Object o)
Dangerous Client can modify anything.
8/9/2019 Prague GWT
34/39
Command Pattern
G (1)
8/9/2019 Prague GWT
35/39
publicclass SimpleGearsDatabase extends Database implements ClientDB {
public SimpleGearsDatabase(String databaseName) throws GearsException { super(databaseName);
} public ResultSet execute(String statement, Object... args) {
String[] strs = new String[args.length];//convert args -> strs
return execute(statement, strs);}
publicvoid createKeyedStringStore(String tableName) {
execute("drop table if exists " + tableName);execute("create table if not exists " + tableName
+ " (key varchar(255), json text )");
} publicvoid addToKeyedStringStore(String tableName, String key,
String value) {execute("insert into " + tableName + " values (?, ?)", key, value);
}
public List getFromKeyedStringStore(String tableName,String key, GearsRowMapper mapper) {
return query("select json from " + tableName + " where key = ?",mapper, key);
}}
Gears (1)
8/9/2019 Prague GWT
36/39
Gears (2) public List query(String sql, GearsRowMapper mapper,
Object... args) {
ResultSet rs = execute(sql, args);
List rtn = new ArrayList();
for (int i = 0; rs.isValidRow(); ++i, rs.next()) {
rtn.add(mapper.mapRow(rs, i));
}
rs.close();
return rtn;
}publicinterface GearsRowMapper {
T mapRow(ResultSet rs, int rowNum) throws DatabaseException;
}
G (3)
8/9/2019 Prague GWT
37/39
Gears (3)public ServiceCache(GWTApp gwtApp) {
this.schoolService = gwtApp.getSchoolService();
this.userService = gwtApp.getUserService();
if (Gears.isInstalled()) {
try {
db = new SimpleGearsDatabase("tocollege.net"); db.createKeyedStringStore(MATCH);
db.createKeyedStringStore(PROCESSTYPE);
} catch (GearsException e) {
Log.warn("No Gears " + e.getMessage());
}
} if (db == null) {
Log.info("Creating Empty Client DB");
db = new EmptyClientDB();
}
}
8/9/2019 Prague GWT
38/39
publicvoid matchSchool(final String query,
final AsyncCallback origCallback) {
List stored = db.getFromKeyedStringStore(MATCH, query,
stringMapper);
if (stored != null && !stored.isEmpty()) {
origCallback.onSuccess(stored);
return;
} else {
schoolService.getSchoolsMatching(query,
new AsyncCallback() {
publicvoid onFailure(Throwable caught) {
origCallback.onFailure(caught);
}
publicvoid onSuccess(List result) {
origCallback.onSuccess(result);
for (String string : result) {
db.addToKeyedStringStore(MATCH, query,
string);
}
}
});
}
}
8/9/2019 Prague GWT
39/39
Thanks!
ToCollege.net &
http://code.google.com/p/tocollege-net/
http://code.google.com/p/tocollege-net/http://code.google.com/p/tocollege-net/