• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13
 

SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13

on

  • 5,586 views

 

Statistics

Views

Total Views
5,586
Views on SlideShare
3,392
Embed Views
2,194

Actions

Likes
3
Downloads
93
Comments
1

5 Embeds 2,194

http://fredsa.allen-sauer.com 2169
http://www.slideshare.net 11
http://theoldreader.com 7
http://1399042838376885139_2c57ba7442213bdde865f1820599e91ce98e16bb.blogspot.com 6
http://localhost.marakana.com 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

11 of 1 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • To the author:

    Some slides are a mess (see 23, 37, 38 for example)
    Same with OO after download. Maybe a PDF export from your office suite would do it look better.

    Thanks
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • quick survey Raise your hand if you've never written a web app using GWT … your organization maintains multiple (>1) GWT apps … uses GWT for all new web apps We have a lot to cover, so let's begin
  • Hope: you've read GWT mission statement Hope: agree that we must FOCUS ON THE USER experience Part of that mission means making you, the developer, more productive
  • In other words, GWT is all about… PRODUCTIVITY FOR DEVELOPERS LIKE YOU AND PERFORMANCE FOR YOUR USERS
  • Google Web Toolkit 101
  • OUR generators, more importantly YOUR generators
  • I'd like to take a moment to talk about a widely misunderstood problem. The problem is of course: WHY IS OUR WEB SITE SO SLOOOOOOOOOW? If HTML + CSS + JavaScript + Images = 40 files, that's 50ms*40=2s Last night I pinged a few servers in Sydney; 380ms * 40 = 15s!!!
  • GWT produces two classes of files: cache/nocache - *cache* => cache forever; new versions receive new file names - *nocache* => must-revalidate Raise your hand if your web server set Expires/Cache-Control headers So how do we create those perfectly cacheable *cache* files?
  • When you're building real apps you need certain things to "just work" Constants, Messages, ConstantsWithLookup, Dictionary, Localizable, DateTimeFormat, NumberFormat
  • That's the productivity, performance and speed pitch Now, let's get to know our toolkit in more detail
  • ensureDebugId() - gives field consistent id for debugging purposes - completely compiled out unless gwt.enableDebugId = true - great testing without the HTML/runtime bloat for users- Your QA guys will HUG YOU for this. Everyone needs a hug, right?
  • You already use some of these. Want to draw attention to this last one. Note this is a GWT Compiler argument, not a javac argument This enables assertions in your compiled (production mode) output
  • What's this second one?
  • We already covered -style PRETTY and -draftCompile for DEVELOPMENT. Hopefully in DEVELOPMENT you also are building just ONE PERMUTATION (i.e. one browser, one language, one logging level) - If not (or for PRODUCTION) use -localWorkers=#cores
  • This is a really useful pattern for keeping your deferred code out of the initial download
  • A very useful tool: - understand why your JavaScript output isn't quite as small (yet) as it could be - why initial download is large
  • Now for something different. Here's a simple JavsScript object in JSON format - {} identify JavaScript objects, [] identify arrays, : for name/value pairs JSON is great - transfer data between client and server OR between servers - Subset of ECMA script so it can be parsed or eval()'d by the browser
  • In JavaScript you have a couple of options 1. Write the whole thing out every time; brittle w.r.t. changes 2. Introduce function overhead Wouldn't it be great if we could have our cake and eat it too? WE CAN!
  • If you're using com.google.gwt.json.JSON library, STOP Use JSO Types instead -You keep the abstractions that make you productive -Your user sees none of that code; only the benefits GREAT for federating/integrating with other systems via web hooks or RESTful interfaces
  • ImageBundles are really easy to create - Declare an interface with a method for each image - That's it!
  • We can of course bundle other resources as well Remember: bundle, bundle, bundle
  • RFC 2397 data: URLs This cursor data: URL can be injected into your injected stylesheet at compile time - You get the convenience of a separate *.cur file - The user either gets a forever cacheable strongly named URL or an inline data: resource - YOU BOTH WIN
  • Locale sensitive names so different languages can have different images Your getWidth()/getHeight() methods automatically adjust
  • How many of you are interested in gadgets? GWT makes it really easy to create them without having to resort to hand written JavaScript
  • - Logging code is partially/completely compiled out in production - Mobile clients log to server - UncaughtExceptionHandler included
  • Add or improve stack traces where browsers are lacking
  • compiler.emulatedStack does add overhead (not for PRODUCTION) - You can just turn on recordLineNumbers (less overhead) - You can already get original method names from the symbol maps
  • This stack trace came from 100% compiled JavaScript - UNKNOWN indicates this is browser land, not your JVM - short, two-letter method names shows OBFUSCATED output - Original Java class/method names injected into source - Copy/paste into your IDE stacktrace console and they are hyperlinked!

SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13 SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13 Presentation Transcript

  • Google Web Toolkit Can help you create amazing apps Can help you create amazing apps
    • San Francisco Java User Group
      • Oct 13, 2009
    Fred Sauer Developer Advocate [email_address] Twitter: @fredsa
  • Agenda
    • Introduction
    • Selecting a language
    • Feature tour
    • Developer productivity
    • Compiler magic
    • Need for speed
    • Q&A
  • Google Web Toolkit Write Core Java APIs Widgets Libraries Java IDEs Debug JVM Debugging Development Mode A real browser Developer Productivity Optimize GWT Compiler Image Bundle (Sprites) CSS Magic End User Performance Run Desktop Mobile Online/Offline Gadgets
  • 3+ years in review May 2006 GWT 1.0 Launch at JavaOne Aug 2008 GWT 1.5 Java 5 language support Apr 2009 GWT 1.7 Dedicated IE8 support Fall 2009 GWT 2.0 LOTS of interesting stuff
  • Mission statement
    • "GWT's mission is to radically improve
    • the web experience for users by
    • enabling developers to use existing
    • Java tools to build no-compromise
    • AJAX for any modern browser."
  • Focus
    • Productivity for developers
      • Language, IDEs, tools, libraries
      • People, ecosystem
    • Performance for your users
      • 'Perfect' caching
      • Whole program optimization
      • Better than practical hand written code
  • No plugins required VML Flash Silverlight
  • What we don't want
  • GWT Browser-Proofs Your JavaScript Code... IE Firefox Safari Chrome Opera
  • Deferred Binding 14800 ms 4836 ms 1997 ms 7148 ms DOM manipulation 2469 ms 1520 ms 918 ms 2477 ms innerText=... - 1386 ms 908 ms - textContent=... 4078 ms 2053 ms 1276 ms 2876 ms Typical portable setInnerText() IE Opera Webkit (Safari) Firefox Improvement 39% 32% 29% 14%
  • A More Powerful Web, Made Easier
  • Eating our own dogfood
  • GWT App Gallery http://gwtgallery.appspot.com / /
  • GWT App Gallery http://gwtgallery.appspot.com / /
  • Selecting a language
  • Rich ecosystem of tools and libraries + + =
  • Code completion and javadoc
  • Can you find the bug? Hint: JavaScript is a dynamic language
  • Catch errors at compile time Java is a static language
  • Feature tour
  • Pluggable Architecture
  • More than just a compiler Development Mode
  • Sprites for free 20,558 bytes 6,824 bytes 11 separate images 1 bundled image
  • Without ClientBundle
  • Roundtrips are deadly
    • 1 round trip for TCP connection setup
    • (1 round trip for HTTP redirect?)
    • 1 round trip for each HTTP request
    $ ping ohare.comPING ohare.com (70.142.247.22): 56 data bytes64 bytes from 70.142.247.22: icmp_seq=0 ttl=113 time= 54.689 ms 64 bytes from 70.142.247.22: icmp_seq=1 ttl=113 time= 55.500 ms 64 bytes from 70.142.247.22: icmp_seq=2 ttl=113 time= 54.728 ms
  • Reducing round trip time
    • Use HTTP Expires and Cache-Control headers
    • Use GWT 'Perfect Caching'
      • Cache 'forever' - your entire app and all its resources
      • Bundle, bundle, bundle
    • Enable HTTP Pipelining
    • Use multiple hostnames
      • Defeat browser's two connection limit
      • Watch out for DNS lookup overhead though
  • Know your HTTP Headers
    • <!-- See RFC 2616. Here's an example:
    • Expires: Thu, 02 Sep 2010 03:21:55 GMT
    • Cache-Control: public, max-age=31536000
    • -->
    • <Files *.cache.* >
    • ExpiresDefault &quot;now plus 1 year&quot;
    • </Files>
    • <Files *.nocache.* >
    • ExpiresDefault &quot;access&quot;
    • </Files>
  • History just works
  • Rich Text Area
  • RTL, I18N, L10N, A11Y
  • Widget Libraries
      • GWT  ( http://code.google.com/webtoolkit/ )  
      • Incubator    ( http://code.google.com/p/google-web-toolkit-incubator/ )
      • Smart GWT  ( http://code.google.com/p/smartgwt/ )
      • GWT-Ext ( http://code.google.com/p/gwt-ext/ )
      • Vaadin (IT Mill Toolkit)  ( http://vaadin.com / )
      • GWT mosaic  ( http://code.google.com/p/gwt-mosaic/ )
      • Ext GWT  ( http://extjs.com/products/gxt/ )
      • Advanced GWT Components
    •      ( http://advanced-gwt.sourceforge.net / ) 
  • GUI Editing
  • Simple, Powerful RPCs
    • interface SpellService extends RemoteService {
    • /**
    •   * Checks spelling and suggests
    •   * alternatives.
    •   * @param the word to check
    •   * @return the list of alternatives
    •   */
    • String[] suggest(String word);
    • }
  • Shameless plugs for: gwt-dnd, gwt-log, gwt-voices gwt-dnd, gwt-log, gwt-voices
  • Developer productivity for you for you
  • GWT 2.0 operating modes
    • Development Mode (Hosted Mode)
      • All about productivity
      • Java + JavaScript
      • Now in any supported browser
    • Production Mode (Web Mode)
      • All about performance
      • Compiled, Pure JavaScript
  • Development Mode (Hosted Mode)
    • JVM debugging
      • Server & Client code in the same IDE
      • Step in / over / out
      • Introspect & modify variables
      • Hot swap code (ignore the IDE warning!)
    NOT TRUE
  • Development Mode Code Server Web Server Duke, the Java mascot Copyright © Sun Microsystems Inc., all rights reserved. Java Virtual Machine
  • Google Plugin for Eclipse
  • Eclipse plugin highlights
    • Extensive JSNI support
    • RPC sync/async quick fixes
    • Launch configurations
    • GWT JUnit tests
    • Contributor SDKs (gwt-user, gwt-dev- <platform> )
    • Development Mode (GWT 2.0)
      • Hosted Mode (GWT 1.7)
    • Constantly improving; check back often
  • GWT Debugging in the browser
  • Complete Java development stack
  • Developer productivity on larger projects on larger projects
  • Do or do not. There is no try.
    • Test, test, test
      • JUnit, CI, TDD
      • Selenium, WebDriver, enableDedugId
      • http://mymachine:8080/Foo.html ?gwt.codesvr= …
      • Code coverage
    • Logging
      • http://code.google.com/p/gwt-log/ ; incubator logging
  • Do or do not. There is no try.
    • gwt-api-checker
    • Design
      • EventBus, MVP, embrace asynchrony
    • Separation & specialization
      • CSS, HTML, Java/JavaScript; UiBinder; MVP
  • Testing & TDD tools your should know
    • GWTTestCase extends UnitTest
    • GWTMockUtilities - neutralizing GWT.create()
    • FakeMessagesMaker
    • Lightweight metrics
      • http://code.google.com/p/google-web-toolkit/wiki/LightweightMetricsDesign
  • Modules you should know
    • <!-- Selenium, WebDriver, etc. -->
    • <inherits name=&quot;com.google.gwt.user. Debug &quot; />
    • <set-property
      • name=&quot; gwt.enableDebugId &quot; value=&quot;true&quot; />
    • // Setup my ids for QA
    • Button searchButton = new Button();
    • searchButton. ensureDebugId (&quot;srch&quot;);
  • Compiler arguments you should know
    • // Story of your compile
    • -soyc
    • // Symbols maps, SOYC reports, RPC info
    • -extra stuff
    • -logLevel
    • // Note: use as a GWT Compiler argument!
    • -ea
  • -extra files you should know
    • stuff/…/…GreetingService .rpc.log
    • stuff/…/rpcPolicyManifest/ manifest.txt
    • stuff/…/ soycReport /splitPoints0.xml.gz
    • stuff/…/symbolMaps/04065D…5D6433CF. symbolMap
    • stuff/…/symbolMaps/24B10B…3859D5C9.symbolMap
    • stuff/…/symbolMaps/2814EF…CF2746BA.symbolMap
    • stuff/…/symbolMaps/32793E…8754FAC1.symbolMap
    • stuff/…/symbolMaps/C23998…9827C30B.symbolMap
  • Collaboration hints you should know
    • Source control, use svn:ignore
      • war/WEB-INF/lib/<sdk-jars>
      • war/WEB-INF/classes
      • war/<module-name>
    • OS/platform browser issues / setup, code formatting
      • trunk/gwt/eclipse/README.txt
    • Eclipse, Checkstyle
      • Prefs: Java Editor -> Save Actions
  • Project Organization
    • war/ layout
    • Multiple modules, single compile, single script
    • Java package naming conventions
      • client
      • server
      • rebind
      • model
  • Configuration parameters you should know
    • <extend-configuration-property
      • name=&quot; rpc.blacklist &quot;
      • value=&quot;com.foo.myapp.client.WidgetList&quot; />
    • <extend-configuration-property
      • name=&quot; rpc.blacklist &quot;
      • value=&quot;com.foo.myapp.client.TimerList&quot; />
  • System properties you should know
    • // Specify benchmark output directory
    • -Dcom.google.gwt.junit.reportPath= …
    • // watch the compiler optimize a method
    • -Dgwt.jjs.traceMethods= foo.Bar.onModuleLoad
  • Compiler arguments you should know
    • // Human readable code
    • -style PRETTY
    • // fast (for development)
    • -draftCompile
    • // Compile N permutations in parallel
    • // (N = number of cores)
    • -localWorkers= N
  • RunAsync package pattern - Gateway class
    • package com.foo.client.addressbookasync;
    • public class AddressBook {
    • // Can't construct directly
    • private AddressBook() { }
    • // Only callable once you have an instance
    • public show() {
    • // use package restricted code here
    • }
  • RunAsync package pattern - Gateway class
    • // Async interface to obtain an
    • // instance of AddressBook
    • public interface Callback {
    • void onCreated (AddressBook addressBook);
    • void onCreateFailure (Throwable e);
    • }
  • RunAsync package pattern - Gateway class
    • public static void get (final Callback cb) {
    • GWT.runAsync(new RunAsyncCallback() {
    • public void onSuccess() {
    • cb.onCreated(new AddressBook());
    • }
    • public void onFailure(Throwable e) {
    • cb.onCreateFailure(e);
    • }
    • });
    • }
    • }
  • Compiler magic
  • GWT quote to remember
    • “The fastest code is that
    • which does not run.”
      • Joel Webber
        • GWT co-creator
  • Conventional and non-conventional
    • Java transformations
      • Dead-code elimination
      • Method inlining
      • Constant folding & propagation
    • JavaScript transformations (gzip motivated)
      • Method reordering
      • Argument renaming
  • EXPERIMENTAL arguments you should know
    • // Don't care what Class#getName() returns?
    • -XdisableClassMetadata
    5% - 10% script reduction Showcase metadata before Showcase metadata after
  • EXPERIMENTAL arguments you should know
    • // In a real-world (very large) Google app...
    • // 1% script size reduction
    • // 10% faster in performance-sensitive code
    • -XdisableCastChecking
    • try {
    • ((Quacker) animal).quack();
    • } catch (ClassCastException c) {
    • Window.alert(&quot;Found a non-quacker&quot;);
    • }
  • Need for speed
  • Speed matters perceived as instantaneous. maintains the feeling that a single task is being carried out. limit for keeping user’s attention. 0.1 seconds 1 second 10 seconds
  • Another reason speed matters 2. UI Changes 3. User Learns 1. User Action Performance for your users
  • GWT Helps Apps Startup More Quickly 26-Nov 29-Apr 18-Jun 28-Jul 12-Sep 27-Oct 24-Dec 16-Mar Size of Initial JavaScript Download (KB) 375 750 1125 1500 0 7x Decrease In Initial Download Size with runAsync() 1400 KB 200 KB
  • Developer guided code splitting
  • Developer guided code splitting
  • Manual code splitting - Don't try this at home
  • Developer guided code splitting
    • GWT. runAsync (new RunAsyncCallback() {
    •   public void onSuccess() {
    •     …
    •   }
    •   public void onFailure(Throwable caught) {
    •     …
    •   }
    • });
  • Story of Your Compile (SOYC)
    • -C
  • Disappearing code that just works
  • JSON - JavaScript Object Notation
    • {
    • &quot;firstName&quot;: &quot;Fred&quot;,
    • &quot;lastName&quot;: &quot;Sauer&quot;,
    • &quot;contactInfo&quot;:
    • {
    • &quot;email&quot;: &quot; [email_address] &quot;, …
    • }
    • &quot;likes&quot;:
    • [
    • &quot;Open Source&quot;, &quot;GWT&quot;, &quot;Chocolate&quot;
    • ]
    • }
  • Using JSON
    • // 1. Server responds
    • {&quot;firstName&quot;: &quot;Fred&quot;, &quot;lastName&quot;: &quot;Sauer&quot;, … }
    • // 2. Client parses
    • p = safeJsonParse(responseText);
    • // or Client evaluates (Danger, Will Robinson!)
    • p = eval(responseText);
  • Using JSON // Brittle var greeting = 'Hello, ' + p.firstName + ' ' + p.lastName; // Additional payload for user to download function getName(p) { return p.firstName + ' ' + p.lastName; } // Additional method invocation overhead var greeting = 'Hello, ' + getName(p);
  • The amazing disappearing code trick Using JSO Types Using JSO Types
    • // This class is about to disappear!
    • public class Person extends JavaScriptObject {
    • // assume 'this' has 'firstName' property
    • public native String getFirstName()
    • /*-{ return this. firstName; }-*/;
    • public native String getLastName()
    • /*-{ return this. lastName; }-*/;
  • The amazing disappearing code trick
    • public class Person extends JavaScriptObject {
    • // This method is about to disappear as well!
    • public String getDisplayName() {
    • return getFirstName() + &quot; &quot; + getLastName();
    • }
    • }
  • The amazing disappearing code trick
    • // Go ahead, use your type-safe abstractions
    • // You get to keep your wrapper classes
    • Person person;
    • // Also keep your convenience methods
    • String name = person.getDisplayName() ;
    • // GWT performs disappearing trick
    • var name = p.firstName + ' ' + p.lastName ;
  • ClientBundle
  • Sprites for free ( ImageBundle )
    • public interface MyDialogImages
    • extends ImageBundle {
    • // Look for 'saveButton.{png|jpg|gif|bmp}'
    • AbstractImagePrototype saveButton();
    • AbstractImagePrototype okButton();
    • @ Resource (&quot;com/foo/myapp/btn_cancel.png&quot;)
    • AbstractImagePrototype cancelButton();
    • }
  • Injecting / inspecting resources at compile-time
    • interface MyBundle extends ClientBundle {
    • public static final MyBundle INSTANCE =
    • GWT.create(MyBundle.class);
    • @Source(&quot;app_config.xml&quot;)
    • TextResource appConfig1();
    • @Source(&quot;wordlist.txt&quot;)
    • ExternalTextResource wordlist();
  • Injecting / inspecting resources at compile-time
    • interface MyBundle extends ClientBundle {
    • @Source(&quot;myCursor.cur&quot;)
    •   DataResource myCursor();
    • }
    • MyBundle.INSTANCE.myCursor().getUrl();
    • http: //localhost:8080/foo/B349934EA27D6EFFD949B88E6A116ED7. cache .cur
    • data: content/unknown;base64,c2FkIGprZ2xkZdqZGZrb
    • CBnamtkZmxnIGRma2xnaiBrZGxmZ2prbGRmamcga2xkaiBna
    • 2w7ZGZqZy…IGRmamtsw7ZGZqZyBrbGRmZ2ogZGtsO
  • Injecting / inspecting resources at compile-time
    • interface MyBundle extends ClientBundle {
    • @Source(&quot;arrow.png&quot;) // locale sensitive!
    •   @ImageOptions( flipRtl = true)
    •   ImageResource pointer();
    • }
    • MyBundle.INSTANCE.pointer().getUrl();
    • MyBundle.INSTANCE.pointer().getWidth();
    • MyBundle.INSTANCE.pointer().getHeight();
  • Injecting / inspecting resources at compile-time
    • interface MyBundle extends ClientBundle {
    • @ Strict // or @NotStrict
    • @Source(&quot;address_book.css&quot;)
    • CssResource addressBookCSS();
    • }
  • Compile time CSS awesomeness http://code.google.com/p/google-web-toolkit/wiki/CssResource http://code.google.com/p/google-web-toolkit/wiki/CssResource
    • @def small 1px; /* Constants */
    • @if user.agent safari gecko1_8 { … }
    • @if locale en { … }
    • @noflip { … } /* no automatic left/right swap */
    • @external
  • Gadgets
  • Corporate Gadgets
  • Making gadgets
    • @ModulePrefs (
    •   title = &quot;Hello World!&quot;,
    •   directory_title = &quot;My first gadget&quot;,
    •   screenshot = &quot;gadget.png&quot;,
    •   thumbnail = &quot;thumb.png&quot;,
    • height = 210)
    • public class HelloGadget
    • extends
    • Gadget<HelloPreferences>
    • {
  • Making gadgets
    • @ModulePrefs(…)
    • public class HelloGadget extends Gadget<HelloPreferences> {
    • public void onModuleLoad() { /* ... */}
    •   protected void init(
    • final HelloPreferences prefs) {
    • }
    • }
  • Making gadget preferences
    • public interface MealPreferences extends UserPreferences {
    •   @PreferenceAttributes(
    • display_name = &quot;Vegetarian&quot;,
    • default_value = &quot;false&quot;)
    •   BooleanPreference noMeat();
    • }
  • Expecting the unexpected
  • Expecting the unexpected in Development
    • public void onModuleLoad () {
    • // This isn't going to help us now
    • GWT.setUncaughtExceptionHandler(…);
    •   // Your initialization code goes wrong here :(
    • // Cross your fingers
    • }
    public void onModuleLoad() { Log. setUncaughtExceptionHandler() ;  DeferredCommand.addCommand(new Command() {    public void execute() {      onModuleLoad2();    }  }); }private void onModuleLoad2 () {  // Your module initialization code goes here}
  • Expecting the unexpected in Development Mode
    • // Hyperlink your stack traces
    • Throwable#printStrackTrace()
  • Expecting the unexpected in Development Mode
    • // Logging with zero overhead in production mode
    • <inherits
      • name=&quot;com.allen_sauer.gwt.log.gwt-log-DEBUG&quot;/>
    • Log.debug(&quot;This is a 'DEBUG' test message&quot;);
  • Make the compiler output human readable
    • // -draftCompile -style PRETTY
    • function $onModuleLoad() {
    • $showArea(getClientWidth(),getClientHeight());
    • }
    • function $showArea(width, height){
    • alert_0('area=' + width * height);
    • }
    // Production Mode (-style OBF) Ee='area=';function nc(){$wnd.alert(Ee+(u(new q),Hb($doc))*Gb($doc))}
  • Expecting the unexpected in Production Mode
    • <inherits name=&quot; compiler.emulatedStack &quot; />
    • <set-configuration-property
    • name=&quot;compiler.emulatedStack. recordLineNumbers &quot;
    • value=&quot;true&quot; />
    • <set-configuration-property
    • name=&quot;compiler.emulatedStack. recordFileNames &quot;
    • value=&quot;true&quot; />
  • Expecting the unexpected in Production Mode
    • Even with -style OBF
      • Server side symbol maps, e.g.
      • xQ() -> MyClass.myOriginalJavaMethod()
  • Expecting the unexpected in Production Mode
    • You still get stack traces in JavaScript!
      • Throwable#getStackTrace()
    • Development Mode
    • Declarative UI (UiBinder)
    • Developer Guided Code Splitting
    • ClientBundle
    • SOYC
    Roadmap Fall 2009
  • Thank you
    • Read more
        • http://code.google.com/webtoolkit/
    • Contact info
        • Fred Sauer
        • Developer Advocate
        • [email_address]
        • Twitter @fredsa
    • Questions?