Você está na página 1de 39

Real GWT Applications

Jeff Dwyer

Author: Pro Web 2.0 Applications with GWT

Founder: ToCollege.net & MyHippocampus.com

Currently: (rails)
Today
• What and Why GWT?
• Demo ToCollege.net
• Real Apps
• Integration Patterns
• Hibernate
• Security
• Gears
Goal

• Rich web experiences.


• Speed and responsiveness of desktop.
• Benefits of the web.
Landscape
JavaScript+ Server RichClient

YUI Flex
Dojo Silverlight
Rico Echo GWT

Script.aclo.us Lazlo
jMaki JSF
Why GWT
• GWT Brings Java Software Development
Best Practices to AJAX HTML
• GWT is DHTML + JavaScript
• JavaScript doesn’t scale... with increased
development staff.
• Best Practices: i18n, History, ImageBundle
Features
• Easy RPC: Single domain for client & server
• History Mechanism
• Extremely Cacheability
• Browser Compatibility
• Real Debugging & JUnit & I18N
Benefits & Joys
• One Domain Model
• Static Typing
• IDE Refactoring, Code Completion
• FindBugs, PMD, Checkstyle, Metrics
• Hosted Mode. Real Debugging.
• Maven
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...
How it Works

• Swing + CSS
• Java to JavaScript compiler
• Compressed and Obfuscated by default
• Include in HTML with <script> and <div>
• Locale Specific Compilation
Demo

• www.ToCollege.net
• http://code.google.com/p/tocollege-net/
ToCollege.net Stack
• MySQL / Hibernate
• Compass
• Spring MVC 2.5
• Acegi & OpenID
• SiteMesh & FreeMarker
• GWT 1.5 Integration SEO, Security
• Also: Facebook, Maven
VerticalLabel
VerticalLabel.java
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<Character, AbstractImagePrototype> 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));
}
}
}
Deferred Binding
• 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);
}-*/;
public class ForumApp extends GWTApp implements
HistoryListener {
public ForumApp(int pageID) {
History
if (initToken.length() > 0) {
onHistoryChanged(initToken);
(Back Button)
}
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);
}
}
http://www.tocollege.net/forums.html#SchoolForumPost~112
JRE SampleApp.java
Emulation 1.5 Source
GWT
Compiler

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

Gecko Gecko
IE (En) ... (...)
(Fr) (En)
JRE SampleApp.java
Emulation 1.5 Source
GWT
Compiler

Gecko Gecko IE (Fr)


function gGd(a){return (this==null?
null:this)===(a==null?null:a)}
function hGd(){return gmb}

... (...) IE (En) function iGd(){return this.$H||(this.


$H=++Cvc)}

Spring MVC, PHP, Rails, etc

IE
JRE SampleApp.java
Emulation 1.5 Source
GWT
Compiler

Gecko Gecko IE (Fr)


function gGd(a){return (this==null?
null:this)===(a==null?null:a)}
function hGd(){return gmb}

... (...) IE (En) function iGd(){return this.$H||(this.


$H=++Cvc)}

Spring <html>
<script language='javascript'
MVC src='com.apress.progwt.Sample

PHP App.nocache.js'></script>
<div id=”gwt-slot”></div>
Rails </html> IE
Simple GWT Integration
protected void 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);
}
Real Applications
• Integration
• JavaScript & FreeMarker Macros
• Hibernate
• Command Pattern
• Security
• XSRF & Command Pattern
• Gears
Integration
• div id == Java Code
• What about multiple component talking to
each other?
• What about re-use?
• This is where people screw up. Many
modules, etc.
• Solution? Leverage JavaScript.
Spring MVC Integration

• Macros are your friend


• Component-ization
• GWT-RPC
• SEO w/ Bootstrapping & <noscript>
Using GWT: Macros
<#macro image imageName>
<@widget "ImageBundle", {"name":"${imageName}"}/>
</#macro>

<#list viewUser.processTypes as processType>


<td>
<#assign processVal = sap.getTheProcess(processType)?default("")/>
<#if processVal?has_content>
<#assign pct = processVal.pctComplete * 100/>
<@gwt.image "pctStatus${pct?int}"/>
<#else>
<@gwt.image "pctStatus0"/>
</#if>
</td>
</#list>
Using GWT: JavaScript
FreeMarker
<html>
<script>
Vars['widgetCount'] = "2"
Vars['widget_1'] = "ImageBundle"
Vars['widget_2'] = "VerticalImage"
Vars['widget_2_text'] = "Make Me Vertical!"
</script>
<script src=”net.tocollege.gwt.Dispatcher”></script>
<div id="gwt-slot-1"></div>
<div id="gwt-slot-2"></div>
</html>

GWTDispatcher

VerticalImageApp ToCollegeApp ImageBundleApp


Hiberate: Architecture


Hibernate4GWT
GWT Client Server
GWT
User RPC User
&
Application Hib4GWT
Application
1..1 1..1

School School

Hib4GWTProxy Reviews (Lazy)


Hibernate w/
GWTHibernateFilter
GWT Client Server
GWT
User User
RPC
&
GWTHibernateFilter
Application Application
1..1 1..1

School School

Null Reviews (Lazy)


Pro & Cons
Hibernate4GWT GWTHibernateFilter

Pros Can use save(Object) No change to objects

Overhead.
Can’t simply call save()
Cons Must implement
Lazy collections != null
Hibernate4GWT
RPC Service
public interface GWTSchoolService extends RemoteService {

SiteCommand executeAndSaveCommand(SiteCommand comm)


throws SiteException;

List<School> getAllSchools() throws SiteException;

PostsList getForum(String tagname, int start, int max)


throws SiteException;

School getSchoolDetails(String schoolName);

List<String> getSchoolsMatching(String match) throws SiteException;

List<ProcessType> matchProcessType(String queryString)


throws SiteException;
}
“Get” Idempotent
SOFE

Spring Security

Server
PostsList getForum(String tagname, int start, int max)
throws SiteException;

School getSchoolDetails(String schoolName);


“Post”
SOFE

Command

Spring Security

XSRF Protection

Server
SiteCommand executeAndSaveCommand(SiteCommand comm)
throws SiteException;
Command Pattern

• Hibernate Integration
• Authorization
• XSRF
• Gears / Offline
save(Object o)

• Dangerous
• Client can modify anything.
Command Pattern


Gears (1)
public class 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);
}
public void createKeyedStringStore(String tableName) {
execute("drop table if exists " + tableName);
execute("create table if not exists " + tableName
+ " (key varchar(255), json text )");
}
public void addToKeyedStringStore(String tableName, String key,
String value) {
execute("insert into " + tableName + " values (?, ?)", key, value);
}

public <T> List<T> getFromKeyedStringStore(String tableName,


String key, GearsRowMapper<T> mapper) {
return query("select json from " + tableName + " where key = ?",
mapper, key);

}
}
Gears (2)
public <T> List<T> query(String sql, GearsRowMapper<T> mapper,
Object... args) {

ResultSet rs = execute(sql, args);


List<T> rtn = new ArrayList<T>();

for (int i = 0; rs.isValidRow(); ++i, rs.next()) {


rtn.add(mapper.mapRow(rs, i));
}
rs.close();
return rtn;
}

public interface GearsRowMapper<T> {


T mapRow(ResultSet rs, int rowNum) throws DatabaseException;
}
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();
}
}
public void matchSchool(final String query,
final AsyncCallback<List<String>> origCallback) {
List<String> stored = db.getFromKeyedStringStore(MATCH, query,
stringMapper);
if (stored != null && !stored.isEmpty()) {
origCallback.onSuccess(stored);
return;
} else {
schoolService.getSchoolsMatching(query,
new AsyncCallback<List<String>>() {
public void onFailure(Throwable caught) {
origCallback.onFailure(caught);
}
public void onSuccess(List<String> result) {
origCallback.onSuccess(result);
for (String string : result) {
db.addToKeyedStringStore(MATCH, query,
string);
}
}
});
}
}
Thanks!

• ToCollege.net &
• http://code.google.com/p/tocollege-net/

Você também pode gostar