gwt extreme!

51

Upload: cromwellian

Post on 14-Dec-2014

15.208 views

Category:

Technology


4 download

DESCRIPTION

In this session, see Google Web Toolkit used in exotic and creative ways to solve interesting engineering problems, from authoring OpenSocial apps that run as both Web gadgets and native Android applications, to developing Adobe AIR applications using GWT, compiling CSS selectors to Javascript at compile time, running multithreaded code with GWT and Gears workers, or exporting GWT libraries for JavaScript users. Learn the secrets of writing "faster than possible" GWT code, how to use Generators and Linkers in harmony, and make seamless procedure calls from GWT code to other environments like Flash, Gears, or Android.

TRANSCRIPT

Page 1: GWT Extreme!
Page 2: GWT Extreme!

GWT Extreme!Performance and FlexibilityRay Cromwell, CTO, Timefire, Inc05/29/2008

Page 3: GWT Extreme!

BUDD: Why did he call it GWT Extreme?

BILL: I guess he thought it sounded kinda cool.

Page 4: GWT Extreme!

Extreme Performance and Flexibility

• “Faster (and Smaller) than Possible” code• Parsing and Evaluating CSS Selector Queries• High Performance Graphics

• Bridging GWT with other execution environments• Remote procedure calls to Android, Flash, and Gears

• Writing Flash and Gears Workers in Java• Tricks to compile subsets of your source as separate

packages• Q&A if time permits

Page 5: GWT Extreme!

GWT Bloated and Slow? Think Again

• Faster• Generate Java code on the fly at compile time• Compiler inlines small Java and Javascript methods• Devirtualizes most other method calls• And tons more, too many to list….

• Smaller• Prunes and eliminates anything not used• Aggressively obfuscates code

• Obfuscated identifiers chosen to maximize DEFLATE compression• Replace runtime operations with compile time

“Faster and Smaller than Possible” Code

Page 6: GWT Extreme!

GwtQuery (aka GQuery)

• JQuery-like API in GWT• Uses GWT 1.5 language features for succinct code• Run-time CSS3 Selector parsing and evaluation• Compile-time CSS3 Selector parsing• Leverages modern browser features when available:

• XPath (document.evaluate)• W3C CSS Selector API (document.querySelectorAll)• getElementsByClassName()

Page 7: GWT Extreme!

GwtQuery Example

Runtime Evaluation

• Find all “menus”, make first element text “Hello”

import gwtquery.client.GwtQuery;import static gwtquery.client.GwtQuery.$;

public class GwtQueryModule implements EntryPoint { public void onModuleLoad() { $(“ul.menu > li:first-child”).html(“hello”); }}

Page 8: GWT Extreme!

GwtQuery Example

What does the $(selector) function translate into?

• On Safari 3.1• document.querySelectorAll(selector)• On Firefox, Opera, older Safari• Lots of Regexp parsing to compute xpathExpression• document.evaluate(xpathExpression)• On IE < 8 and others, a bunch of Regexp and DOM calls• Note: Each browser gets separately compiled JS

Page 9: GWT Extreme!

Digression: Generators

• Feature of Deferred Binding Mechanism• GWT.create(RequestedType.class)• Replace requested type with generated implementation• Turns into new GeneratedType()

Page 10: GWT Extreme!

GwtQuery Example

Compile-time parsing

• Selectors usually literal strings, known at compile time• Why parse and translate these at runtime?!• Perform parsing and translation to XPath or JS-DOM

navigation calls at compile-time

public MySelectors extends Selectors { @Selector(“ul.menu > li:first-child”) GQuery allFirstMenuItems();}

MySelectors selector = GWT.create(MySelectors.class);

selector.allFirstMenuItems().html(“hello”);

Page 11: GWT Extreme!

GwtQuery Example

Compile-time parsing

• GWT.create(MySelectors.class) triggers Generator• Different Generator for each browser (DOM, XPath, etc)• Generator creates implementation of MySelectors interface• For each method with a @Selector annotation• Parses CSS Selector, emits implementation of method• Compiler inlines method, elides everything else

Page 12: GWT Extreme!

GwtQuery Example

Compile Time Parsing: Bottom Line

ul > li:first-child

becomes

document.evaluate(“ul/li[position() = 1]”)

Page 13: GWT Extreme!

GwtQuery Example

How big is the output?

• jQuery 1.2.3• ~3400 lines of Javascript• 98kb unprocessed source• 15kb obfuscated and gzipped• GwtQuery• ~3400 lines of Java• 116kb on disk• How big is it compiled?• 15kb or larger?• How about 7kb, a 50% reduction?• 3kb sounds about right!• 712 bytes is the answer!• Smaller than single packet, HTTP headers

Page 14: GWT Extreme!

Caveats and Objections

• Trivial example• More reasonable example would exercise larger part of API• and reduce amount of pruned code• Example shows how aggressive GWT optimizer is• past GWT critics have used Hello World examples to

demonstrate purported “bloat”

Page 15: GWT Extreme!

Demo: Progressively Enhancing <UL> into PowerPoint-like slide transitionsSample Code

<div class="slide transition-fade"> Slide 1 <ul class="transition-fade"> <li>Point One</li> <li>Point Two</li> <li>Point Three</li> </ul></div><div class="slide transition-fade"> Slide 2 <ul class="transition-appear"> <li>Slide 2 Point One</li> <li>Slide 2 Point Two</li> <li>Slide 3 Point Three</li> </ul></div>

Page 16: GWT Extreme!

GwtQuery code

Sample Code for Selectors needed

interface Slide extends Selectors { // find all LI elements rooted at ctx @Selector("li") NodeList<Element> slideBulletsCtx(Node ctx);

// Find all DIV elements with class 'slide' @Selector("div.slide") NodeList<Element> allSlides(); }

Page 17: GWT Extreme!

GwtQuery code

Sample Code for Event Handling

final Slide s = GWT.create(Slide.class);$(Document.get().getBody()).click(new Function() { int curSlide = 0; int curBullet = 0; GQuery slides = $(s.allSlides()); GQuery bullets = $(s.slideBulletsCtx(slides.get(curSlide)));

public boolean f(Event e) { if (curBullet < bullets.size()) bullets.eq(curBullet++).as(Effects).fadeIn(); else bullets.css("opacity","0"); slides.eq(curSlide).css("display", "none"); curSlide++; if(curSlide == slides.size()) curSlide = 0; curBullet = 0; bullets = $(s.slideBulletsCtx(slides.get(curSlide))); slides.eq(curSlide).css("display","block") .as(Effects).fadeIn(); return true; }});

Page 18: GWT Extreme!

Sample of Generated Output for Selector “.slide li”

W3C Selector API version

public class SlideNativeImpl extends SelectorEngine implements MySelectors { public final NodeList<Element> slideBullets() { return querySelectorAll(".slide li"); }}

Page 19: GWT Extreme!

Sample of Generated Output for Selector “.slide li”

XPath Version

public class SlideXPathImpl extends SelectorEngine implements Slide { public final NodeList<Element> slideBullets() { return xpathEvaluate("./descendant::*[contains(concat(' ', @class, ' '), ' slide ')]/descendant::li"); }}

Page 20: GWT Extreme!

GWT Query

• Alpha Released Today• Available at http://gwtquery.com• Help Contribute to Core and Plugins!

Page 21: GWT Extreme!

High Performance Graphics

Page 22: GWT Extreme!

Extreme Graphics Performance

• Immediate Mode vs Retained Mode– Canvas vs SVG/VML

• JS VMs top out at between 1000-10000 method calls on Canvas

• Can we cache/reuse work?• Take a page from OpenGL’s Playbook:

Display Lists

Page 23: GWT Extreme!

Extreme Graphics Performance: Display Lists

• Record a sequence of draw calls• Replay sequence like a macro• Framework/driver can perform optimizations

– Collapse sequence of scale/rotate/translate operations into setTransform call

– Cull/Clip some operations if possible– Cache result as bitmap where possible

• DEMO!

Page 24: GWT Extreme!

Extreme Graphics Performance and Flexibility:Chronoscope Chart Library

• 20,000 lines of code– 500kb of source– compiles to 58kb compressed

• 30,000+ points at interactive rates• Javascript, Flash, Applet, Servlet, and Android• Multi-grained API

– Full control with Java+GWT– Higher level with Javascript API– Or with no coding at all via Microformats and CSS

• DEMO!

Page 25: GWT Extreme!

Some insights from porting to GWT 1.5

• Method inlining yields up to 100% perf gains• Ability to inline JSNI provided huge gains for

IE/Flash version• Slight increase in size • Abstractions don’t hurt much• Beware usage of long, costs of non-double

types• Static initializers can be harmful

Page 26: GWT Extreme!

Chronoscope and Mobile Code with GWT

• Platform neutral code • Only 4 classes needed to run in other

environments or on the “cloud”– Flash, Android, Applet, Servlet

• GWT methods automatically exported to Javascript via GWT-Exporter library

Page 27: GWT Extreme!

Integrating GWT with other Environments

Page 28: GWT Extreme!

Integrating GWT with other environments

• GWT already integrates with Servlets• JSNI is used to integrate with Browser/JS• Extend RPC model to arbitrary containers

– Android– Flash– Gears Workers

Page 29: GWT Extreme!

Example: Syndroid

Gadgets can span many Platforms

Android iGoogle OSX GoogleDesktop

Yahoo

And many Flash widget startups

Page 30: GWT Extreme!

Example: Syndroid

• Synthesis of Android and GWT• Write Gadgets to a restricted API• Compile to Javascript or Dalvik or (later)

Actionscript• Deploy on Android, iGoogle, OSX, Yahoo,

et al• Prototype DEMO!

Page 31: GWT Extreme!

Example: Android Native Interface

What if Android browser had a window.locationService object?

public class LocationServiceClient implements LocationService { public native String getLocation() /*-{ return $wnd.locationService.getLocation(); }-*/;}

Page 32: GWT Extreme!

Generators + Linkers = Awesomeness

• Define RPC interface (sync or async)• Use Generator to generate client stub

• Params may or may not need to be serialized• Also generate “server” to handle request• “Server” handling code may need

bootstrapping• Linker used to generate bootstrap code• If needed, use Linker to package/compile

Page 33: GWT Extreme!

Android Native InterfaceAndroidNativeInterface

LocationService

String getLocation();

extends

LocationServiceClient

String getLocation() { return $wnd.locationService.getLocation();}

implements

Emitted by Generator

LocationServiceImpl

String getLocation() { return ...android native code...}

impl

Written by Developer

Android Bootstrap

Bind LocationServiceImpl towindow.locationService

Provided by Linker

Page 34: GWT Extreme!

Example: Android Native Interface

Synchronous RPC

public interface AndroidNativeInterface {}

@ANIBinding("locationService")@ImplementedBy("LocationServiceImpl")public interface LocationService extends AndroidNativeInterface { String getLocation();}

Page 35: GWT Extreme!

Example: Android Native Interface

Implement Android side (not legal GWT code!)

public class LocationServiceImpl implements LocationService { public String getLocation() { try { Location currentLocation = ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) .getCurrentLocation("gps");

return "Lat: "+currentLocation.getLatitude()+ ", Long: "+currentLocation.getLongitude(); } catch (Throwable e) { return "ExceptionInNativeAndroid:"+e.getClass().getName()+": "+e.getMessage(); } }}

Page 36: GWT Extreme!

Example: Android Native Interface

To use

LocationService service = GWT.create(LocationService.class);Window.alert(“Your location is: “+service.getLocation());

Page 37: GWT Extreme!

Example: Linker Generated BootstrapActivity

LocationServiceImpl gets exposed as window.locationService

public class BootstrapActivity extends Activity { @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(getBootstrapView()); } public WebView getBootstrapView() { WebView wv=new WebView(this); wv.getSettings().setJavaScriptEnabled(true); wv.getSettings().setUseDesktopUserAgent(true); wv.getSettings().setLoadsImagesAutomatically(true); wv.addJavascriptInterface(new ContactsServiceImpl(this), "contactsService"); wv.addJavascriptInterface(new LocationServiceImpl(this), "locationService"); wv.loadUrl("file:///android_asset/index.html"); return wv; }}

@ImplementedBy

@ANIBinding

Page 38: GWT Extreme!

Example: Linker Generated Manifest

Needed by Android

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="syndroid.demo.SyndroidDemo"><uses-permission android:name="android.permission.ACCESS_LOCATION"/><uses-permission android:name="android.permission.ACCESS_GPS"/><uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS"/> <application> <activity android:name="BootstrapActivity" android:label="SyndroidDemo"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application></manifest>

Page 39: GWT Extreme!

Example: Linker Generated Build Script

Used to compile and package APK file

mkdir resmkdir classes$ANDROID_HOME/tools/aapt compile -m -J src -M AndroidManifest.xml -I $ANDROID_HOME/android.jarfind src -name '*.java' -print >src.listjavac -cp $ANDROID_HOME/android.jar @src.list -d classes$ANDROID_HOME/tools/dx -JXmx384M --dex --output=classes.dex -- locals=full --positions=lines classes/$ANDROID_HOME/tools/aapt package -f -c -M AndroidManifest.xml -A assets -I $ANDROID_HOME/android.jar SyndroidDemo.apkzip SyndroidDemo.apk classes.dex

Page 40: GWT Extreme!

Client Stub Generator

• Use @ImplementedBy annotation to find Android implementing class

• Copy this class as resource (for Linker step)• Generate new Client stub

– Generate JSNI native method on Client Stub– JSNI method invokes Javascript object named by

@ANIBinding annotation

Page 41: GWT Extreme!

Generator to Linker Communication

• Generators work on types, can’t see build artifacts (compiler output)

• Linkers work out build artifacts, can’t explore the type system

• Some Linkers need type information– e.g. Bootstrap scripts, manifests

• Communicate with linker by emitting a build artifact (e.g. text file)

• Linker can scan for this special file and read contents

• In our case, we write out mapping of @ANIBinding to @ImplementedBy

Page 42: GWT Extreme!

Using Linker Generate Bootstrap/Package for APT

• Read info from Generator (ANIBindings/ImplementedBy)

• Copy implementation classes to build dir• Copy public resources to assets dir• Generate Android Manifest.xml• Generate Android Bootstrap Activity

– Set WebView as view– Enable Javascript– For each ANIBinding, bind instance of

implementing class to Javascript• Run Android Tools

– javac– dx– aapt to create final .apt

Page 43: GWT Extreme!

Future Directions

• Use Special GWT bootstrap script to detect Android browsers

• Offer user choice of “over the air” provision of Android-specific app

• See http://timepedia.blogspot.com/2008/01/project-syndroidsynthesis-of-gwt-and.html for detailed architecture

Page 44: GWT Extreme!

Compiling GWT to Flash

Page 45: GWT Extreme!

Reusing the same Design Pattern for Flash

• Define ActionScriptNativeInterface• Place all “Flash” java code in .flash package

– Use generator to copy it to output as a resource• Use Generator to create JSNI stubs to invoke

Flash methods• But how do we turn Java code into SWF for

the server side?

Page 46: GWT Extreme!

ActionScript Native InterfaceActionScriptNativeInterface

DrawService

void drawLine(x,y);

extends

DrawServiceClient

void drawLine(int x, int y) { $wnd.flashPlugin.drawLine(x, y);}

implements

Emitted by Generator

package foo.flash;

native void drawLine(int x, int y) /*-{ ... actionscript code to draw line ... }-*/;

impl

Written by Developer

Flash Bootstrap

generate main()

Export drawLine(x,y) via ExternalInterface

Provided by Linker

Page 47: GWT Extreme!

Compiling Java to ActionScript with GWT

• IDEA: Invoke GWT compiler recursively• Use Linker to create new GWT Project • Copy “flash” Java code to source path • Emit Module.gwt.xml file• Generate entrypoint

– For each ActionScriptNativeInterface add ExternalInterface

• Invoke GWT Compiler to produce SWF?• Emit SWF as output artifact

Page 48: GWT Extreme!

Packaging GWT output as SWF?

• GWT Compiler produces JS• Avoid GWT APIs which call DOM JSNI

methods– JRE generally safe

• Use Linker to– add custom bootstrap script to invoke EntryPoint– invoke Flex3 SDK in non-strict mode– Emit resulting SWF as output artifact

• Generator+Linker+Generator+Linker Pattern

Page 49: GWT Extreme!

Writing Gears Workers in GWT

• Reuse same pattern as Flash– e.g. place Gears-translatable Java in ‘.gears’

package• Use Async RPC interfaces• JSON to marshal simple object types to Gears

and back• Use SSO-like Linker

Page 50: GWT Extreme!

Q&A?

• GwtQuery available at– http://gwtquery.com

• Chronoscope available – http://timepedia.org

• For in-depth articles/code, see blog at:– http://timepedia.blogspot.com

• Upcoming articles on Syndroid, and ActionScriptNativeInterface

Page 51: GWT Extreme!