You are on page 1of 305

web-development using

GWT + mvp4g

Anthony Kotenko, iPark ventures™


2011 ©
This presentation was specially prepared for
Application Developer Days 2011
Saint-Petersburg, Russia
Slides — there

goo.gl/4GgnS
[ PDF, ~2MB ]
Anthony Kotenko
6 years in Java EE development
6 years in UI development

Sex: male
Anthony Kotenko
6 years in Java EE development
6 years in UI development

Sex: male

http://shamansir.madfire.net
http://zokotuhaFly.habrahabr.ru
http://twitter.com/shaman_sir
http://profiles.google.com/shaman.sir
Schedule
1. Introduction. A brief history and
examples of GWT usage

2. A brief introduction to concepts:


- MVP / Reverse MVP
- EventBus
- Dependency Injection

3. Description of mvp4g framework


- differences in RMVP implementation from GWT
- difference in implementation of EventBus
- multimoduleness
- HistoryConverter conception
- advantages / disadvantages
4. Components in GWT
- UiBinder, standard components
- custom widgets development

5. Our layouting system development

6. Working with non-Java Server-Side-API


(versus RPC-services)
- making call chains
- callback vs. GwtEvent
- advantages / disadvantages

7. i18n in GWT

8. Conclusion. Links to the examples


Your questions may (and must be) asked during the lecture:
to let reporter know about you having question regarding the theme or not,
just raise your hand (any of them).

Like so:

I have a question!

Discussion is important
1. Briefly 'bout GWT
Google
GWT Web
Toolkit

/ˈɡwɪt/
code.google.com/webtoolkit/
GWT
/ˈɡwɪt/
/ˈɡwɪt/
Used in these projects:
Google Wave wave.google.com

Google Checkout checkout.google.com

Google Moderator google.com/moderator

Whirled whirled.com

Lombardi Blueprint blueprint.lombardi.com

ContactOffice beta.contactoffice.com
Used in these projects:
GoGrid gogrid.com

Curriki curriki.org

OpenKM openkm.com

Kdice kdice.com

SeeMap seemap.ru

Одноклассники odnoklassniki.ru
A box of useful tools
The most complete
A box of useful tools

for web-developer
widgets

http://old.ongwt.com/public/WindowsLiveWriter_GWTMosaicnicewidgetlibrary_D777_image_2.png
widgets
optimization

http://radar.oreilly.com/200912081729.jpg
widgets
optimization
cross-browserly!

http://2.bp.blogspot.com/_VzXmgKXrn6Y/TKB2a3eZTBI/AAAAAAAAAaQ/mn7NmabwO8U/s1600/browsers%5Btsksoft.blogspot.com%5D.jpg
widgets
optimization
cross-browserly!
on-the-fly development

http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug

http://www.ibm.com/developerworks/library/j-ajax4/eclipse-debug.jpg
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug

RPC
API/DB
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug

RPC simple i18n

http://test.ical.ly/wp-content/uploads/2010/04/i18n.png
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug

RPC simple i18n


Code Splitting
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug

RPC simple i18n


Code Splitting
http://www.safetylca.org/images/toolbox.jpg
http://blog.ericlamb.net/wp-content/uploads/2009/08/toolbox.jpg
http://www.safetylca.org/images/toolbox.jpg
/ˈɡwɪt/
GWT is a box of useful tools
History
Version 1.0
was released in 2006
Version 1.0
was released in 2006

Version 1.6
Google Eclipse Plugin
Project structure matches
Web Application specification
Version 2.0
- Development Mode
- Code Splitting
- Declarative UI
- Client Bundle
Version 2.1
MVP-conception
RequestFactory / Editors
Version 2.1
MVP-conception
RequestFactory / Editors

Version 2.2
UI Designer
HTML5 Canvas support
only Java 1.6 was left
quake2-gwt-port.appspot.com

Quake 2 in browser

https://y2mzuw.blu.livefilestore.com/y1m9Pc7Xi6qtMdKU_hGCv7VZrEKiIZTYqDIC5laL9A0LxracK6AN8EAet90MRtWlBJISphNp_Y8QCxpb0p9v1y
lTCdwATCweUs9496DC14_ijBjhnpj2oRWme9B1SC2C0t9HJ1wX8RTsVQAhyDYCsqfeg/quake-html5-04-02-2010.jpg
quake2-gwt-port.appspot.com

Quake 2 in browser
Trendy
Youth
Modern
/ˈɡwɪt/
GWT is actively developing project,
however it already contains
everything you need
2. GWT Conceptions
almaer.com/blog/rotating-java-and-javascript-on-the-server
EntryPoint
Any GWT-project starts from entry point
● Java → JavaScript, JSNI
● Development Mode

● Code Splitting

● <Module>.gwt.xml

● MVC, MVP, RMVP, EventBus

● Deferred Binding

● Dependency Injection

● Remote Service

● JUnit

● Disadvantages and remarks


JavaScript Logo is from marakana.com
JavaScript Native Interface

JSNI
public native static void <functionName>(<parameters>) /*-{

. . .

@<path.to.the.package>.<ClassName>::<methodName>(
/L<param-type>;/L<param-type>;...)(<arguments>);

. . .

}-*/;

JSNI
public native static void getJson(int requestId, String url,
StockWatcher handler) /*-{
var callback = "callback" + requestId;

var script = document.createElement("script");


script.setAttribute("src", url+callback);
script.setAttribute("type", "text/javascript");

window[callback] = function(jsonObj) {
handler.@com.google.gwt.sample.stockwatcher
.client.StockWatcher::handleJsonResponse(
Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
window[callback + "done"] = true;
}

setTimeout(function() {
if (!window[callback + "done"]) {
handler.@com.google.gwt.sample.stockwatcher
.client.StockWatcher::handleJsonResponse(
Lcom/google/gwt/core/client/JavaScriptObject;)(null);
}

document.body.removeChild(script);
delete window[callback];

JSNI
delete window[callback + "done"];
}, 1000);

document.body.appendChild(script);
}-*/;
You can wrap native JavaScript widgets with JSNI.
For example, Google Maps or any WYSIWYG-editor

JSNI
WYSIWYG-widget,
written in Closure
and integrated with the help of JSNI
Google Maps widget,
integrated with the help of JSNI

Flash-object
using JavaScript-callbacks,
which are called through JSNI JSNI
You can use JSNI to
make third-party components
written in JavaScript
become GWT-widgets
Development Mode


Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start


Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start

the plugin for your lovely IDE


the plugin for your lovely browser
(but not for Opera)


Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start

http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start

http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
Development Mode helps in debugging your project:
when you change your Java-code
just press Ctrl+F5 in your browser —
and your changes will come into force! Mrrwah!
Code Splitting

when the neccessary code was loaded


Code Splitting
Spell isoid createAsync(final MClient client) {

    GWT.runAsync(...)
      public void onFailure(Throwable err) {
        . . .
      }

      public void onSuccess() {


        if (instance == null) {
        . . .  instance = new Module();
        . . .
        client.onSuccess(instance);
      }
    });
}
when the neccessary code was loaded
Code Splitting
Spell isSp createAsync(final MClient client) {

    GWT.runAsync(new RunAsyncCallback() {
      public void onFailure(Throwable err) {
        . . .
      }

      public void onSuccess() {


        if (instance == null) {
        . . .  instance = new Module();
        . . .
        client.onSuccess(instance);
      }
    });
}
when the neccessary code was loaded
Code Splitting
public static void createAsync(final MClient client) {

    GWT.runAsync(new RunAsyncCallback() {
      public void onFailure(Throwable err) {
        client.onUnavailable();
      }

      public void onSuccess() {


        if (instance == null) {
          instance = new Module();
        }
        client.onSuccess(instance);
      }
    });
}
when the neccessary code was loaded
Code Splitting lets you
specify separate parts of your project to load:
so you can split your project in large modules —
and your users will feel themselves happy! Brrlyawrr!
<Module>.gwt.xml
<Module>.gwt.xml
⁕ Components

a list of code components you use


<Module>.gwt.xml
⁕ Components

a list of code components you use

⁕ Browsers

a list of browsers to be a target of project compilation


<Module>.gwt.xml
⁕ Components

a list of code components you use

⁕ Browsers

a list of browsers to be a target of project compilation

⁕ Locales

a list of locales supported in your project


<Module>.gwt.xml
⁕ Components

a list of code components you use

⁕ Browsers

a list of browsers to be a target of project compilation

⁕ Locales

a list of locales supported in your project

⁕ Debug

turning debug information output on/off


<Module>.gwt.xml
⁕ Components

<inherits name="com.google.gwt.user.User"/>

⁕ Browsers

⁕ Locales

⁕ Debug
<Module>.gwt.xml
⁕ Components

<inherits name="com.google.gwt.user.User"/>

⁕ Browsers

<set-property name="user.agent"
value="ie6,gecko1_8,safari" />

⁕ Locales

⁕ Debug
<Module>.gwt.xml
⁕ Components

<inherits name="com.google.gwt.user.User"/>

⁕ Browsers

<set-property name="user.agent"
value="ie6,gecko1_8,safari" />

⁕ Locales

<extend-property name="locale" values="fr_CA,de" />


<set-property-fallback name="locale" value="fr_CA" />

⁕ Debug
<Module>.gwt.xml
⁕ Components

<inherits name="com.google.gwt.user.User"/>

⁕ Browsers

<set-property name="user.agent"
value="ie6,gecko1_8,safari" />

⁕ Locales

<extend-property name="locale" values="fr_CA,de" />


<set-property-fallback name="locale" value="fr_CA" />

⁕ Debug [<Module>Debug.gwt.xml]
<Module>.gwt.xml
⁕ Components

<inherits name="com.google.gwt.user.User"/>

⁕ Browsers

<set-property name="user.agent"
value="ie6,gecko1_8,safari" />

⁕ Locales

<extend-property name="locale" values="fr_CA,de" />


<set-property-fallback name="locale" value="fr_CA" />

⁕ Debug [<Module>Debug.gwt.xml]

<inherits name="com.example.MainModule" />


<set-property name="log_level" value="DEBUG" />
<Module>.gwt.xml
⁕ Components

<inherits name="com.google.gwt.user.User" />

⁕ Browsers

<set-property name="user.agent"
value="ie6,gecko1_8,safari" />

⁕ Locales

<extend-property name="locale" values="fr_CA,de" />


<set-property-fallback name="locale" value="fr_CA" />

⁕ Debug [<Module>Debug.gwt.xml]

<inherits name="com.example.MainModule" />


<set-property name="log_level" value="DEBUG" />
.gwt.xml file — is almost the same thing as
web.xml file for web application:
here lies all of the project configuration
R
M
V
P
Model
View
Controller
ModelAPI/DB

View
Controller
Model
View
calls

Controller
Model
View
Presenter
Model
View
events
update

Presenter
Reverse
Model
View
Presenter
Reverse
Model
View
Presenter
Presenter
Event Bus
er Presenter Pre
Event Bus
P P P P P P P
EB
Hmmm... To be honest, seems
I did not understand the difference
between all those mah-fah-am-wee-pee...
Difference between MVC and MVP

geekswithblogs.net/kobush/archive/2006/01/09/65305.aspx
The article about MVC/MVP difference

geekswithblogs.net/kobush/archive/2006/01/09/65305.aspx
The video with the example of EventBus in work

tv.jetbrains.net/videocontent/gwt-event-bus-basics
The video with the example of EventBus in work

tv.jetbrains.net/videocontent/gwt-event-bus-basics
EventBus is the central communication channel
Deferred Binding
Deferred Binding
In response to the lack of Reflection
Deferred Binding
In response to the lack of Reflection

Dynamic implementation of any


interface (and just in case if it is actually
required)
Deferred Binding
In response to the lack of Reflection

Dynamic implementation of any


interface (and just in case if it is actually
required)

GWT.create(....class) spell
Deferred Binding
In response to the lack of Reflection

Dynamic implementation of any


interface (and just in case if it is actually
required)

GWT.create(....class) spell

CompileTime-binding
Deferred Binding
In response to the lack of Reflection

Dynamic implementation of any


interface (and just in case if is actually
required)

GWT.create(....class) spell

CompileTime-binding
Deferred Binding
PopupImpl: public void setVisible(boolean visible) {
// ... common code for all implementations of PopupPanel ...

// If the PopupImpl creates an iframe shim, it's also


// necessary to hide it as well.
impl.setVisible(getElement(), visible);
}
Deferred Binding
PopupImpl: public void setVisible(boolean visible) {
// ... common code for all implementations of PopupPanel ...

// If the PopupImpl creates an iframe shim, it's also


// necessary to hide it as well.
impl.setVisible(getElement(), visible);
}

PopupImplIE6: public native void setVisible(Element popup,


boolean visible) /*-{
if (popup.__frame) {
popup.__frame.style.visibility = visible ? 'visible' : 'hidden';
}
}-*/;
Deferred Binding
<replace-with class="com.google.gwt...PopupImplIE6">
<when-type-is class="com.google.gwt...PopupImpl" />
<any>
<when-property-is name="user.agent" value="ie6" />
<when-property-is name="user.agent" value="ie6_1" />
</any>
</replace-with>
Deferred Binding
<replace-with class="com.google.gwt...PopupImplIE6">
<when-type-is class="com.google.gwt...PopupImpl" />
<any>
<when-property-is name="user.agent" value="ie6" />
<when-property-is name="user.agent" value="ie6_1" />
</any>
</replace-with>

private static final PopupImpl impl = GWT.create(PopupImpl.class);


Slides on Deferred Binding

www.docstoc.com/docs/53396874/Deferred-Binding-The-Magic-of-GWT
Slides on Deferred Binding

www.docstoc.com/docs/53396874/Deferred-Binding-The-Magic-of-GWT
Deferred Binding is a tool to create cross-browser
and translingual implementations.
Namely for techniques that will differ between
contexts of project usage.
Attention!
★ Attention ★
Dependency Injection
Dependency Injection
Using GWT INjection / Guice frameworks
Dependency Injection
Using GWT INjection / Guice frameworks

Binding instances to interfaces at one point


(thus we achieve separation of behavior
from implementing solution)
Dependency Injection
Using GWT INjection / Guice frameworks

Binding instances to interfaces at one point


(thus we achieve separation of behavior
from implementing solution)

Lets you forget about XML-settings and


factories
Dependency Injection
Using GWT INjection / Guice frameworks

Binding instances to interfaces at one point


(thus we achieve separation of behavior
from implementing solution)

Lets you forget about XML-settings and


factories

Runtime-binding
Dependency Injection
Using GWT INjection / Guice frameworks

Binding instances to interfaces at one point


(thus we achieve separation of behavior
from implementing solution)

Lets you forget about XML-settings and


factories

Runtime-binding
Dependency Injection
class MyModule extends AbstractGinModule {
@Override
protected void configure() {
bind(Something.class).toProvider(SomethingProvider.class);
bind(Any.class).in(Singleton.class);
bind(Foo.class).to(SomeFooImpl.class);
}
}

class Bar {
@Inject private Any any;
private final Something something;
private final Foo foo;

@Inject
public Bar(Something something, Foo foo) {
}
}
Dependency Injection

@GinModules(MyModule.class)
interface class MyGinjector extends Ginjector {

public Something getSomething();


public Foo getFoo();

}
Guice wiki-pages

code.google.com/p/google-guice/wiki/Motivation?tm=6
Dependency Injection lets you easily manage
your logical implementations.
Even when your project is running.

For example, you can switch


database drivers or
service implementations.

These are the things you've done


using application-context.xml
in Spring, but much better.

Annotations are wonderrrful!


Remote Service
Remote Service
public interface StringReverserService extends RemoteService {
public String reverseString(String stringToReverse);
}
Remote Service
public interface StringReverserService extends RemoteService {
public String reverseString(String stringToReverse);
}

public interface StringReverserServiceAsync {


void reverseString(String stringToReverse, AsyncCallback async);
}
Remote Service
public interface StringReverserService extends RemoteService {
public String reverseString(String stringToReverse);
}

public interface StringReverserServiceAsync {


void reverseString(String stringToReverse, AsyncCallback async);
}

public class StringReverserServiceImpl extends RemoteServiceServlet


implements StringReverserService { . . . }
Remote Service
public interface StringReverserService extends RemoteService {
public String reverseString(String stringToReverse);
}

public interface StringReverserServiceAsync {


void reverseString(String stringToReverse, AsyncCallback async);
}

public class StringReverserServiceImpl extends RemoteServiceServlet


implements StringReverserService { . . . }

StringReverserServiceAsync reverserService =
(StringReverserServiceAsync) GWT.create(StringReverserService.class);
Tutorial on creating Remote Services

developerlife.com/tutorials/?p=125
Remote Services is server-side API
built using Java interfaces
JUnit
JUnit
public class StockWatcherTest extends GWTTestCase {

public String getModuleName() {


return "com.google.gwt.sample.stockwatcher.StockWatcher";
}

. . .

}
JUnit
public class StockWatcherTest extends GWTTestCase {

. . .

FooPresenter.IFooView fooView =
Mockito.mock(FooPresenter.IFooView.class);
... = new FooPresenter(..., fooView);
Mockito.verify(fooView).someViewMethod(...);
}
GWT-code is easy to test
because of JUnit support
Deficiencies and observations
Anyway, you need good skills in JavaScript
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler

JavaScript-errors provide little information


Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler

JavaScript-errors provide little information


Although, they are for sure much easier to understand when debug info is on
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler

JavaScript-errors provide little information


Although, they are for sure much easier to understand when debug info is on

Development Mode works slower than real code:


so you may encounter problems in events succession
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler

JavaScript-errors provide little information


Although, they are for sure much easier to understand when debug info is on

Development Mode works slower than real code:


so you may encounter problems in events succession
Нey, you say you've never debugged code using alerts?!
Regular expressions are just a wrapper for JavaScript RegExp Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler

JavaScript-errors provide little information


Although, they are for sure much easier to understand when debug info is on

Development Mode works slower than real code:


so you may encounter problems in events succession
Нey, you say you've never debugged code using alerts?!
Regular expressions are just a wrapper for JavaScript RegExp Deficiencies and observations
Anyway, you need good skills in JavaScript

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility

No guidance at non-Java Server-Side (Python & GAE, for example)

One JS-error crashes the whole application

JavaScript-errors provide little information

Development Mode works slower than real code:


so you may encounter problems in events succession
Regular expressions are just a wrapper for JavaScript RegExp Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries

«Manual» makeup (HTMLPanel) becomes not sustainable


and has no cross-browser compatibility
GWT is for web-applications, but not pretentious portals
You can write your own (or extend existing) components,
but then thinking about cross-browser compatibility is your concern
And you'll need to make a detailed lecture about GWT components system
for your makeup men, by the way

No guidance at non-Java Server-Side (Python & GAE, for example)


Well, let's buid it on top of RequestBuilder

One JS-error crashes the whole application


However, we have GWT.setUncaughtExceptionHandler

JavaScript-errors provide little information


Although, they are for sure much easier to understand when debug info is on

Development Mode works slower than real code:


so you may encounter problems in events succession
Нey, you say you've never debugged code using alerts?!
Here GWT drawbacks are discussed

www.linux.org.ru/forum/talks/4497412
Every reasoned weakness in GWT
have a rational solution.
Summary on GWT-code optimization

galak-sandbox.blogspot.com/2010/10/gwt.html
And I would ask for a beer now!
3. mvp4g
mvp4g framework web-page

code.google.com/p/mvp4g/
● What helps?
● Annotation system
● RMVP realization
● EventBus realization
● URL, HistoryConverters, #!
● Multimodularity
● PlaceService
● Remarks
mvp4g framework helps to
mvp4g framework helps to

work with (R)MVP


mvp4g framework helps to

work with (R)MVP

organize multi-modular applications


mvp4g framework helps to

work with (R)MVP

organize multi-modular applications

design and develop events buses


mvp4g framework helps to

work with (R)MVP

organize multi-modular applications

design and develop events buses

work with history (incl. hashbangs #!)


mvp4g framework helps to

work with (R)MVP

organize multi-modular applications

design and develop events buses

work with history (incl. hashbangs #!)

... also constantly being improved


mvp4g framework helps to

work with (R)MVP

organize multi-modular applications

design and develop events buses

work with history (incl. hashbangs #!)

... also constantly being improved


mvp4g framework showcase

mvp4gshowcase.appspot.com
mvp4g framework showcase

mvp4gshowcase.appspot.com
Comparison of code written with mvp4g or native GWT

code.google.com/p/mvp4g/wiki/Mvp4g_vs_GWTP
Comparison of code written with mvp4g or native GWT

code.google.com/p/mvp4g/wiki/Mvp4g_vs_GWTP
Pierre-Laurent Coirier
plcoirier@gmail.com
Pierre-Laurent Coirier
plcoirier@gmail.com
(meet him at Google I/O '11)
The code you write using mvp4g
framework is much simpler than
GWT-code without its usage.

It is achieved by Pierre correct


use of annotations.

Code
Code is
is written
written by
by human.
human.
ItIt is
is better
better to
to help
help him
him sometimes.
sometimes.
Annotations
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
Annotations @InitHistory
@InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
There is Annotation Processor Factory
(annotation validator that applies
just when you edit the source code)

Annotations
Annotations
Annotations —
— the
the power
power of
of mvp4g!
mvp4g!
RMVP
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
RMVP @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@Presenter(view=OneView.class)
public class OnePresenter extends
BasePresenter<IOneView, OneEventBus> {

@Inject
private ServiceAsync service;
RMVP }
presenter
@Presenter(view=OneView.class)
public class OnePresenter extends
BasePresenter<IOneView, OneEventBus> {

@Inject
private ServiceAsync service;
RMVP }
view class OneView extends Composite
implements IOneView {
. . .
}
@Presenter(view=OneView.class)
public class OnePresenter extends
BasePresenter<IOneView, OneEventBus> {

@Inject
private ServiceAsync service;
RMVP }
reverse class OneView extends Composite
implements IOneView,
ReverseViewInterface<OnePresenter> {
. . .
}
EventBus
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
EventBus @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {

EventBus
@Event
public void fooEvent(...);

@Event
events public void barEvent(...);

}
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {

@Event(handlers={FooPresenter.class,

EventBus AcmePresenter.class})
public void fooEvent(...);

handlers @Event(handlers=BarPresenter.class)
public void barEvent(...);

FooPresenter::onFooEvent(...) {...}
FooPresenter::onFooEvent(...) {...}
BarPresenter::onBarEvent(...) {...}
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {

@Event(handlers={FooPresenter.class,
AcmePresenter.class},

EventBus
activate={FooPresenter.class,
AcmePresener.class},
deactivate={BarPresenter.class})

activation public void fooEvent(...);

@Event(handlers=BarPresenter.class,
activate={BarPresenter.class},
deactivate={FooPresenter.class,
AcmePresener.class})
public void barEvent(...);

}
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {

EventBus @Event(broadcastTo=IBroadcast.class,
calledMethod="boo")
broadcast public void broadcastEvent(...);

public class Foo implements IBroadcast {


public void boo(...) {...};
}
@Filters(filterClasses={FilterOne.class,
FilterTwo.class})
@Debug(logger=CustomLogger.class)
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {

. . .

EventBus
filters and stuff
class FilterOne implements EventFilter<OneEventBus> {

@Override
public boolean filterEvent(...) { return ...; }

}
History
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
History @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@Events(..., historyOnStart = true)
public interface OneEventBus
extends EventBus {

@Start
@InitHistory
public void start();

History @Event(handlers=...,
navigationEvent=true,
historyName="foo",
handling historyConverter=OneHC.class)
public void fooEvent(...);

@NotFoundHistory
public void show404();

}
@Events(..., historyOnStart = true)
public interface OneEventBus
extends EventBus {

@Start
@InitHistory
/start public void start();

History @Event(handlers=...,
navigationEvent=true,
historyName="foo",
passing historyConverter=OneHC.class)
/foo public void fooEvent(...);

@NotFoundHistory
public void show404();

}
@Events(...)
public interface OneEventBus ... {

@Event(..., navigationEvent=true,
historyName="foo",
historyConverter=OneHC.class)
/foo?26;all public void fooEvent(int id,
Filter filter);

@History public class OneHC implements


HistoryConverter<OneEventBus> {

History @Inject private TokenGenerator tokens;

parameters public void convertFromToken(...) {


if ("foo".equals(event))
eventBus.fooEvent(
Integer.parseInt(params[0]),
Filter.parse(params[1]));
}

public String fooEvent(...) {


return tokens.fooEvent(id, filter);
}

}
@Events(...)
public interface OneEventBus ... {

@Event(...)
/#!foo?26;all public void fooEvent(int id,
Filter filter);

History @History public class OneHC implements


HistoryConverter<OneEventBus> {

hashbang ...

public boolean isCrawable() {


return true;
}

}
History
History and
and event
event buses
buses are
are the
the skeleton
skeleton
of
of your
your site
site navigation
navigation system
system
Multi-
modularity
@AfterLoadChildModule
@BeforeLoadChildModule
Multi- @ChildModule
@ChildModules
modularity @DisplayChildModuleView
@HistoryName
annotations @LoadChildModuleErrors
REST
object/action[?parameters]

Multi- company/list
company/add
modularity company/edit?123
user/list
URLs user/add
user/edit?39
public interface UserModule
extends Mvp4gModule {}

public interface CompanyModule


extends Mvp4gModule {}

Multi-
modularity
modules @Events(..., module=CompanyModule.class)
public interface CompanyEventBus
extends EventBus {...}

@Events(..., module=UserModule.class)
public interface UserEventBus
extends EventBus {...}
@Events(...)
@ChildModules(
@ChildModule(moduleClass=UserModule.class)
@ChildModule(moduleClass=CompanyModule.class)
)
public interface ParentEventBus extends EventBus{

@Event(modulesToLoad=UserModule.class)
public void usersList();

@Event(modulesToLoad=CompanyModule.class)

Multi- }
public void companiesList();

modularity
event buses
@Events(..., module=UserModule.class)
public interface UserEventBus extends EventBus {

@Event(..., handlers=UserListPresenter.class,
historyName="list")
public void usersList();
. . .
}
@Events(...)
@ChildModules(
@ChildModule(moduleClass=
UserModule.class,
runAsync=true)
@ChildModule(moduleClass=
CompanyModule.class,
runAsync=true))

Multi- public interface ParentEventBus


extends EventBus {

modularity . . .

asynchronous }
loading
History,
History, event
event buses
buses and
and modules
modules are
are the
the skeleton
skeleton
of
of your
your site
site navigation
navigation system
system

You
You can
can load
load modules
modules asynchronously!
asynchronously!
So
So user
user will
will not
not get
get the
the kilobytes
kilobytes
he
he needs
needs not,
not, ifif he
he will
will not
not visit
visit
specified
specified sections
sections of of your
your site.
site.
PlaceService
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
PlaceService @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@PlaceService(CustomPlaceService.class)
@Events(...)
public interface MainEventBus
extends EventBusWithLookup {
. . .
}

PlaceService
overriding
public class CustomPlaceService
extends PlaceService {
protected void convertToken(String token) { ... }
protected String[] parseToken(String token) { ... }
public String tokenize(String eventName,
String param) { ... }
. . .
}
Remarks
There is no layouting-system yet

Remarks
There is no layouting-system yet

Multimodularity is aimed
at object → action principle

Remarks
There is no layouting-system yet

Multimodularity is aimed
at object → action principle

GIN/Guice are supported

Remarks
There is no layouting-system yet

Multimodularity is aimed
at object → action principle

GIN/Guice are supported

Remarks
When using custom GwtEvents,
you should keep an eye on presenters
activation. Callbacks — easier.
There is no layouting-system yet

Multimodularity is aimed
at object → action principle

GIN/Guice are supported

Remarks
When using custom GwtEvents,
you should keep an eye on presenters
activation. Callbacks — easier.

There are LazyView & LazyPresenter


mvp4g — is what Zoidberg has prescribed!
4. UI components
Button
PushButton
RadioButton
CheckBox
DatePicker
PopupPanel
ToggleButton
StackPanel, StackLayoutPanel
TextBox
HorizontalPanel
PasswordTextBox
VerticalPanel
TextArea
FlowPanel
Hyperlink / Anchor
VerticalSplitPanel
ListBox
HorizontalSplitPanel
CellList
SplitLayoutPanel
MenuBar
DockPanel, DockLayoutPanel
Tree, CellTree
TabPanel, TabLayoutPanel
SuggestBox
DisclosurePanel
RichTextArea
FlexTable, Grid, CellTable
CellBrowser
TabBar
DialogBox
GWT components library

code.google.com/webtoolkit/doc/latest/RefWidgetGallery.html
UiBinder: .ui.xml
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:VerticalPanel styleName="my-css-style">
<g:HorizontalPanel>
<g:Label>Name</g:Label>
<g:TextBox ui:field="nameBox">Babylen</g:TextBox>
</g:HorizontalPanel>

<g:HorizontalPanel>
<g:Label>Family name</g:Label>
<g:TextBox ui:field="fnameBox">Tatarsky</g:TextBox>
</g:HorizontalPanel>

<g:ListBox ui:field="namesLst" visibleItemCount="1" />

<g:Button ui:field="submit">Submit</g:Button>
</g:VerticalPanel>
</ui:UiBinder>
UiBinder: .java
public class SettingsForm extends Composite {
interface SFormBinder extends UiBinder<Widget,
SettingsForm> {}
private static FormBinder uiBinder =
GWT.create(SFormBinder.class);

@UiField TextBox nameBox;


@UiField TextBox fnameBox;
@UiField ListBox namesLst;

public HelloWorld(String... names) {


initWidget(uiBinder.createAndBindUi(this));
for (String name : names) { namesLst.addItem(name); }
}

@UiHandler("submit")
public onSubmit(ClickEvent e) { ... }

}
HTMLPanel

<g:HTMLPanel>
<div>Some div</div>
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<p>
<span>Some span</span>
</p>
</g:HTMLPanel>
Attention!
.b-popup { position: absolute; }

Manual make-up vs. Cross-browser support

.gwt-Button { font-size: 150%; }


In GWT, there is
an entire vast library of components
and an armory of layouts.

Though, the fact of developing a site with


its own unique style is fraught with
cross-browser compatibility problems.
Custom components
Custom components

Do not inherit, but delegate


Custom components

Do not inherit, but delegate

@UiConstructor
public MyCustomWidget(String defaultText) {
. . .
}

public void setMaxLength(int maxLength) { ... }


Custom components

Do not inherit, but delegate

@UiConstructor
public MyCustomWidget(String defaultText) {
initWidget(uiBinder.createAndBindUi(this));
}

public void setMaxLength(int maxLength) { ... }


Attention!

Customization vs. Cross-browser support


So it is better either to keep
your site design as simple as possible...

...either
...either to
to allocate
allocate
aa significant
significant amount
amount ofof time
time
for
for UI
UI designers
designers andand programmers
programmers
to
to work
work through
through your
your own
own
component
component library.
library.
The lack of UI Designer
was a drawback
for someone
The lack of UI Designer
was a drawback
for someone
5. Layouting
Reasons?
Reasons?
If your site is built with logical blocks
which are visually placed in different order,
depending on context.
Reasons?
If your site is built with logical blocks
which are visually placed in different order,
depending on context.

For example, portlets or custom widgets


Reasons?
If your site is built with logical blocks
which are visually placed in different order,
depending on context.

For example, portlets or custom widgets

Means, when it is a flexible site.


Reasons?
If your site is built with logical blocks
which are visually placed in different order,
depending on context.

For example, portlets or custom widgets

Means, when it is a flexible site.

These mechanics are not yet implemented in mvp4g


Layouts

LIST ITEM EDIT

B B
A

A A D
C
C B C
public interface Layout {

Layouts public LayoutId id();


public HasWidgets place(Place place);
Map<Place, HasWidgets> places();
}

LIST ITEM EDIT


public class LayoutList public class LayoutEdit
implements Layout { } implements Layout { }

LayoutList.ui.xml: LayoutEdit.ui.xml:
<FlowPanel ui:field="a"/> <FlowPanel ui:field="a"/>
<FlowPanel ui:field="b"/> <FlowPanel ui:field="b"/>
<FlowPanel ui:field="c"/> <FlowPanel ui:field="c"/>
<FlowPanel ui:field="d"/>
public class LayoutItem
implements Layout { }

LayoutItem.ui.xml:
<FlowPanel ui:field="a"/>
<FlowPanel ui:field="b"/>
<FlowPanel ui:field="c"/>

public enum Place { A, B, C, D};


Layouts

LIST ITEM EDIT

B B
A

A A D
C
C B C
Base page

toolbar

layout

footer
copy

BasePage.ui.xml:
<FlowPanel ui:field="toolbar"/>
<FlowPanel ui:field="layout"/>
<FlowPanel ui:field="footer"/>
<FlowPanel ui:field="copy"/>
Page / Portal
public enum Portal implements MakesLink {

NEWS_LIST(LayoutId.LIST, <event-spec>, <options>),


NEWS_EDIT(LayoutId.EDIT, <event-spec>, <options>),
NEWS_VIEW(LayoutId.ITEM, <event-spec>, <options>),
NEWS_DELETE(LayoutId.ITEM, <event-spec>, <options>)
USER_LIST(LayoutId.LIST, <event-spec>, <options>),
USER_EDIT(LayoutId.EDIT, <event-spec>, <options>),
USER_VIEW(LayoutId.ITEM, <event-spec>, <options>),
USER_DELETE(LayoutId.ITEM, <event-spec>, <options>),
. . .

@Override
public String makeLink() { . . . }

}
Link
public enum Portal implements MakesLink {

. . .

public class PortalUrl implements MakesLink {

PortalUrl(Portal portal[, <params>]) { ... }

public PortalUrl addParam(...) { ... }


public PortalUrl fromEvent(String module,
String event,
String params) { ... }
@Override public String makeLink() { ... }

}
Link
public enum Portal implements MakesLink {

. . .

public class PortalUrl implements MakesLink {

PortalUrl(Portal portal[, <params>]) { ... }

public PortalUrl addParam(...) { ... }


public PortalUrl fromEvent(String module,
String event,
String params) { ... }
@Override public String makeLink() { ... }

} History.newItem(Portal.USER_LIST.makeLink());
History.newItem(new PortalUrl(Portal.USER_EDIT, uid).makeLink());
History.newItem(userTokenGenerator.edit(uid));
Layout builder
public abstract class
LayoutBuilder<E extends ChildEventBus> {

public CanBuildLayout prepareFor(final Portal page) {


return new CanBuildLayout {
public Layout build(State state) {
layout(page, state,
LayoutFactory.get(page.layout).places());
}
}
}

public abstract void layout(Portal page,


State state,
Map<Place, HasWidgets> places);

}
Switching layouts
public class UserHistoryConverter
implements HistoryConverter<UserEventBus> {

public void convertFromToken(String evt, String param) {

// можно использовать tokenGenerator


PortalUrl curUrl =
PortalUrl.fromToken("user",evt,param);
Portal portal = curUrl.portal;

eventBus.newPage(portal,
layoutBuilder.prepareFor(portal));

eventBus.dispatch(curUrl);

}
Builder implementation
public class UserLayoutBuilder
implements LayoutBuilder<UserEventBus> {

public void layout(Portal page, State state,


Map<Place, HasWidgets> places)

switch (page) {
case USER_ITEM: {
eventBus.projectItem(places.get(Place.A));
eventBus.projectCalendar(places.get(Place.B));
eventBus.projectPreview(places.get(Place.C));
} break;
case USER_LIST: switch (state) { ... } break;
case USER_EDIT: ... break;
}

}
Event buses
public interface ChildEventBus {

@Event(forwardToParent=true)
public void newPage(Portal page, CanBuildLayout builder);

@Event(forwardToParent=true)
public void project(Widget what, HasWidgets where);

@Event(forwardToParent=true)
public void updateState(State state)

[ forwarded to BaseEventBus ]
Event buses
public interface UserEventBus extends ChildEventBus {

// navigation
@Event(navigationEvent=true, ...)
public void list();
@Event(navigationEvent=true,
handlers=UserShowPresenter.class,
historyConverter=UserHistoryConverter.class)
public void show(String uid);
@Event(navigationEvent=true, ...)
public void edit(String uid);
. . .

// projection
@Event(handlers=UserShowPresenter.class,calledMethod="prjItem")
public void projectItem(HasWidgets where);
@Event(handlers=UserShowPresenter.class,calledMethod="prjPrvw")
public void projectPreview(HasWidgets where);
@Event(handlers=CalendarPresenter.class,calledMethod="project")
public void projectCalendar(HasWidgets where)

}
Layouting helps us to build and to live!
I've wanted to make a demo for you, but had no time ;(

github.com/shamansir/gwt-mvp4g-layouting-demo
I've wanted to make a demo for you, but had no time ;(

Follow

github.com/shamansir/gwt-mvp4g-layouting-demo
I've wanted to make a demo for you, but had no time ;(

github.com/shamansir/gwt-mvp4g-layouting-demo
Фото gwt-mvp4g-layouting-demo.appspot.com
QRCode gwt-mvp4g-layouting-demo.appspot.com
6. Non-Java API
Accidentally, here is the main point

code.google.com/p/google-web-toolkit-doc-1-5/wiki/GettingStartedJSON
Possibility — exists
RequestBuilder
General context
Call chains
Insering JS-object into HTML-markup

(you can parse them using JSNI)


Advantage : Independence from serialization
Disadvantage :
Unconventional approach with all the consequences
And here is the source code

shamansir-ru.tumblr.com/post/1728720550/deferred-api-gwt-rpc
7. i18n
Messages / Constants
public interface LoginMessages extends Messages {
public String enterName();
public String emailExists(String email);
public String emailInvalid(String email);
public String loginFailed(String username);
public String youFailedNTimes(@PluralCount int times);
}

public interface MenuConstants extends Constants {


public String login();
public String logout();
public String contacts();
public String settings();
}
Messages / Constants
LoginMessages_ru.properties
enterName = Введите имя
emailExists = E-mail {0} зарегистрирован в системе
emailInvalid = Некорректный e-mail {0}
loginFailed = Не удалось зайти пользователем {0}
youFailedNTimes = Кол-во неудачных попыток: {0,number}
youFailedNTimes[one] = {0,number} неудачная попытка
youFailedNTimes[few] = {0,number} неудачных попыток

MenuConstants_ru.properties
login= Войти
logout = Выйти
contacts = Контакты
settings = Настройки
Messages / Constants

LoginMessages messages = GWT.create(LoginMessages.class);


MenuConstants constants = GWT.create(MenuConstants.class);

<ui:with type="....LoginMessages" field="messages" />


<e:MyTextBox ui:field="box" defaultText="{messages.enterText}"
Messages / Constants

<ui:UiBinder ... xmlns:txt="ui:with:...MyMessages" />


<txt:msg key="messageKey">Message</txt:msg>
Messages / Constants

public interface ErrorsConstants extends ConstantsWithLookup {


Map<String, String> errors();
}

ErrorsConstants_ru.properties
ERR_101 = Ошибка авторизации
ERR_102 = Неизвестная ошибка
ERR_103 = Ресурс не найден

errors = ERR_101, ERR_102, ERR_103


Mae'n
Mae'n hawdd
hawdd iawn
iawn ii
gyfieithu
gyfieithu prosiectau
prosiectau GWT...
GWT...

...if
...if you'll
you'll teach
teach your
your
translators
translators to to manage
manage
.properties-files
.properties-files or or give
give
them
them PoEdit
PoEdit or
or Pootle
Pootle
Позволяют использовать локализованные ресурсы

ResourceBundles
8. Conclusion
We applied those techniques for Experika

experika.com
Welcome to Experika

experika.com
Thanks to
Vitaly Gashock, for intoducing mvp4g to me
and partnership
http://twitter.com/vgashock

Mikhail Kashkin, for mentoring


http://www.vurt.ru

Alexey Kakunin, for 道 and 先生


http://www.emdev.ru
Anthony Kotenko

profiles.google.com/shaman.sir
Thank you.

Made with LibreOffice 3.3.1 Impress


More questions?
GWT FTW!
Philip J. Fry, Dr. Zoidber, Nibbler and Hypnotoad
are all the Futurama series characters
and all created by Matt Groening

Homer Simpson is the character


of The Simpsons series
and also created by Matt Groening

All the phrases they tell


in this presentation have no relation
nor for these characters,
nor for series noticed above,
nor for their creator

You might also like