Real GWT Applications

VIInternet και Εφαρμογές Web

12 Φεβ 2012 (πριν από 5 χρόνια και 6 μήνες)

668 εμφανίσεις

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
Dojo
Rico
Script.aclo.us
jMaki
Flex
Silverlight
Echo
Lazlo
JSF
GWT
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
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));
}
}
}
VerticalLabel.java

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
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
SampleApp.java
1.5 Source
JRE
Emulation
GWT
Compiler
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)}
... (...)
SampleApp.java
1.5 Source
JRE
Emulation
GWT
Compiler
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
?
SampleApp.java
1.5 Source
JRE
Emulation
GWT
Compiler
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
<html>
<script language='javascript'
src='com.apress.progwt.Sample
App.nocache.js'></script>
<div id=”gwt-slot”></div>
</html>
... (...)
Spring
MVC
PHP
Rails
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
<#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: Macros
<#macro image imageName>
<@widget "ImageBundle", {"name":"${imageName}"}/>
</#macro>
Using GWT: JavaScript
FreeMarker
GWTDispatcher
VerticalImageApp
ToCollegeApp
ImageBundleApp
<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>
Hiberate: Architecture


Hibernate4GWT
User
Application
School
Hib4GWTProxy
1..1
GWT Client
User
Application
School
Reviews (Lazy)
1..1
Server
GWT
RPC
&
Hib4GWT
Hibernate w/
GWTHibernateFilter
User
Application
School
Null
1..1
GWT Client
User
Application
School
Reviews (Lazy)
1..1
Server
GWT
RPC
&
GWTHibernateFilter
Pro & Cons
Hibernate4GWT
GWTHibernateFilter
Pros
Can use save(Object)
No change to objects
Cons
Overhead.
Must implement
Hibernate4GWT
Can’t simply call save()
Lazy collections ! null
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
Spring Security
Server
SiteCommand executeAndSaveCommand(SiteCommand comm)

throws
SiteException;
Command
XSRF Protection
Command Pattern

Hibernate Integration

Authorization

XSRF

Gears / Offline
save(Object o)

Dangerous

Client can modify anything.
Command Pattern


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 (1)
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/