SlideShare a Scribd company logo
1 of 87
Google Web Toolkit
              0-60 in no time flat
Chris Ramsdale
Developer Relations, Google
new java.util.Date();
Agenda

•   Overview
•   Demo
•   Deeper dive
•   Optimizations for developers
•   Optimizations for users
•   Intro to GWT & MVP
From 25,000 feet

•   Development toolkit, not a framework
•   Code in Java, run as Javascript
•   One codebase, any browser
•   Makes Ajax a piece of cake...and faster
•   Used within many Google products, including
    Google Wave and Ad Words
The GWT Family




   GWT SDK       Google Plugin   Speed Tracer
                  For Eclipse
It’s a loving relationship
It’s a loving relationship



              Develop
It’s a loving relationship



              Develop        Debug
It’s a loving relationship



              Develop          Debug




                        Optimize
Focus on the users

 Our users - developers
 – Leverage existing IDEs and tools
 – Minimize refresh time between codes changes
 – Automate where possible


 Your users - customers
 – Minimize startup time
 – Make it a comfortable experience
 – Allow them to select the browse
Different Goals
Different Goals

Developers
 • Next killer feature
 • Making it look good
 • Code refactoring
Different Goals

Developers               Customers
 • Next killer feature   • Make it fast
 • Making it look good   • ...oh, and don’t charge
                         my credit card twice
 • Code refactoring
Different Goals

Developers               Customers
 • Next killer feature   • Make it fast
 • Making it look good   • ...oh, and don’t charge
                         my credit card twice
 • Code refactoring
No plugins required

                       Silverlight
        VML
               Flash
Nothing against them, but...


        Foo Player not available for
        your device


        We restrict use of
        technologies required by
        products like Foo Player...
Quirkiness


                       Firefox   Webkit (Safari) Opera    IE
    Typical portable   2876 ms   1276 ms        2053 ms   4078 ms
    setInnerText()
    textContent=... -            908 ms         1386 ms   -
    innerText=...      2477 ms   918 ms         1520 ms   2469 ms
    DOM manipulation 7148 ms     1997 ms        4836 ms   14800 ms
Quirkiness


                       Firefox   Webkit (Safari) Opera    IE
    Typical portable   2876 ms   1276 ms        2053 ms   4078 ms
    setInnerText()
    textContent=... -            908 ms         1386 ms   -
    innerText=...      2477 ms   918 ms         1520 ms   2469 ms
    DOM manipulation 7148 ms     1997 ms        4836 ms   14800 ms


    Improvement        14%       29%            32%       39%
Quirkiness


                       Firefox   Webkit (Safari) Opera    IE
    Typical portable   2876 ms   1276 ms        2053 ms   4078 ms
    setInnerText()
    textContent=... -            908 ms         1386 ms   -
    innerText=...      2477 ms   918 ms         1520 ms   2469 ms
    DOM manipulation 7148 ms     1997 ms        4836 ms   14800 ms


    Improvement        14%       29%            32%       39%




    http://quirksmode.org/blog/
Can you find the bug?

 function getMax(values) {
 	
 	    var maximum = values[0];
 	
 	    for (var i = 0; i < values.length; ++i) {
 	    	     if (values[i] > maximum) {
 	    	     	    maxinum = values[i];
 	    	     }
       }
       return maximum;
 }
Can you find the bug?

 function getMax(values) {
 	
 	    var maximum = values[0];
 	
 	    for (var i = 0; i < values.length; ++i) {
 	    	     if (values[i] > maximum) {
 	    	     	    maxinum = values[i];
 	    	     }
       }
       return maximum;
 }

 Hint: Javascript is a dynamic language
...GWT Can




 Java is a static language
Demo time



To see this in action, letʼs:
 • Create a new web app
 • Debug within the browser
 • Update, refresh, run
 • Deploy to JBoss Enterprise Application Platform




                                 14
A Java to Javascript compiler right?
More like assembly-level JS
function rd(a,b){var c;if(b.b){b.b=false;b.c=null}c=b.c;b.c=a.f;try{++a.c;Cd(a.e,b,a.d)}finally{--
a.c;a.c==0&&sd(a)}if(c==null){b.b=true;b.c=null}else{b.c=c}}
function Hb(b,c){yb();$wnd.setTimeout(function(){var a=$entry(Eb)(b);a&&$wnd.setTimeout(arguments.callee,c)},c)}
function ih(a,b){var c,d;if(b.e!=a){return false}try{Vg(b,null)}finally{c=b.f;(d=(hc(),c).parentNode,(!d||d.nodeType!
=1)&&(d=null),d).removeChild(c);ki(a.b,b)}return true}
function hj(c){if(c.length==0||c[0]>ao&&c[c.length-1]>ao){return c}var a=c.replace(/^(s*)/,cn);var b=a.replace(/s*
$/,cn);return b}
function qj(a){var b,c,d,e;b=0;d=a.length;e=d-4;c=0;while(c<e){b=a.charCodeAt(c+3)+31*(a.charCodeAt(c
+2)+31*(a.charCodeAt(c+1)+31*(a.charCodeAt(c)+31*b)))|0;c+=4}while(c<d){b=b*31+a.charCodeAt(c++)}return b|0}
function Bj(a){var b,c,d,e;e=this.v();a.length<e&&(a=Pd(a,e));d=a;c=this.p();for(b=0;b<e;++b)
{Ud(d,b,c.s())}a.length>e&&Ud(a,e,null);return a}
function xl(a){var b,c,d;a.length<this.c&&(a=(c=a,d=Qd(0,this.c),Sd(c.aC,c.tI,c.qI,d),d));for(b=0;b<this.c;++b)
{Ud(a,b,this.b[b])}a.length>this.c&&Ud(a,this.c,null);return a}
function Ud(a,b,c){if(c!=null){if(a.qI>0&&!ee(c.tI,a.qI)){throw wi(new ui)}if(a.qI<0&&(c.tM==gm||c.tI==2)){throw
wi(new ui)}}return a[b]=c}
function hi(a,b,c){var d,e;if(c<0||c>a.c){throw Qi(new Oi)}if(a.c==a.b.length){e=Rd(we,
47,8,a.b.length*2,0);for(d=0;d<a.b.length;++d){Ud(e,d,a.b[d])}a.b=e}++a.c;for(d=a.c-1;d>c;--d)
{Ud(a.b,d,a.b[d-1])}Ud(a.b,c,b)}
function th(b,c){var i;rh();var a,e,f,g,h;e=null;for(h=b.p();h.r();){g=fe(h.s(),8);try{c.q(g)}catch(a)
{a=Ge(a);if(ie(a,11)){f=a;!e&&(e=Fl(new Dl));i=Sj(e.b,f,e)}else throw a}}if(e){throw sh(new oh,e)}}
function Tj(j,a,b,c){var d=j.b[c];if(d){for(var e=0,f=d.length;e<f;++e){var g=d[e];var h=g.y();if(j.x(a,h)){var
i=g.z();g.A(b);return i}}}else{d=j.b[c]=[]}var g=Ul(new Sl,a,b);d.push(g);++j.e;return null}
function bk(a){var b,c,d;if((a==null?null:a)===this){return true}if(!(a!=null&&de(a.tI,18))){return false}c=fe(a,
18);if(c.v()!=this.v()){return false}for(b=c.p();b.r();){d=b.s();if(!this.u(d)){return false}}return true}
function Cd(a,b,c){var d,e,f,g,h,i,j;g=b.j();d=(h=fe(Nj(a.b,g),4),!h?0:h.c);if(c){for(f=d-1;f>=0;--f)
{e=(i=fe(Nj(a.b,g),4),fe((Nk(f,i.c),i.b[f]),13));b.i(e)}}else{for(f=0;f<d;++f){e=(j=fe(Nj(a.b,g),
4),fe((Nk(f,j.c),j.b[f]),13));b.i(e)}}}
function Fb(a){var b,c,d,e,f,g;b=false;d=a.length;f=(new Date).getTime();while((new Date).getTime()-f<100)
{for(c=0;c<d;++c){g=a[c];if(!g){continue}if(!g[0].h()){a[c]=null;b=true}}}if(b){e=[];for(c=0;c<d;++c){if(!a[c])
{continue}e[e.length]=a[c]}return e}else{return a}}
function cf()
{cf=gm;af={};bf=[];af[pn]=[lg,kg,mg];af[qn]=[tg,sg];af[rn]=[xg,wg];af[sn]=[Bg,Ag,Cg];_e(bf,se,pn);_e(bf,te,qn);_e(bf,u
e,rn);_e(bf,ve,sn)}                                       16
I can haz pretty print plz




                             Simply adjust
                             the output
                             style of the
                             compiler via
                             the plugin




                       17
Yes, a Java to Javascript compiler
function init(){
  $GreetingService_Proxy(new GreetingService_Proxy);
  $add_0(($clinit_99() , get_0(null)), $Label(new Label, 'Foo'));
  $wnd.alert('foo');
}

function caught_0(e){
  if (e != null && canCast(e.typeId$, 11)) {
    return e;
  }
  return $JavaScriptException(new JavaScriptException, e);
}

function $RemoteServiceProxy(this$static){
  return this$static;
}

function RemoteServiceProxy(){
}                                 18
But wait...there’s more
A deeper dive
From Eclipse to your browser


 Eclipse




                                                                     Browser Plugins
 package com.google.gwt.samples.eclipsecon.client;



                                                              TCP
 import com.google.gwt.core.client.EntryPoint;
 import com.google.gwt.core.client.GWT;

 @SuppressWarnings("unused")
                                                     Code
 public class EclipseCon implements EntryPoint {
                                                     Server
     public void onModuleLoad() {
       Window.alert("foo");
     }


                                                              HTTP
 }


                                                      Jetty
                                                     Server




                                                        21
From Eclipse to deployment




Your code...                                        Generators   Translators   Linkers
package com.google.gwt.samples.eclipsecon.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;

@SuppressWarnings("unused")
public class EclipseCon implements EntryPoint {

    public void onModuleLoad() {
      Window.alert("foo");
    }
}




                                                         22
From Eclipse to deployment




Your code...                                        Generators   Translators   Linkers
package com.google.gwt.samples.eclipsecon.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;

@SuppressWarnings("unused")
public class EclipseCon implements EntryPoint {

    public void onModuleLoad() {
      Window.alert("foo");
    }
}




                                                         23
Provide the power behind your
GWT app

Automate away boilerplate code

Foundation for permutations




24
Use Case: GWT RPCs


Typical Ajax call


            Serialization         Serialization
               Code                  Code
   Client                   XHR                   Server




                            25
Use Case: GWT RPCs


GWT-enabled Ajax


                      Serialization         Serialization
          GWT Proxy




                                                            GWT Proxy
                         Code                  Code
 Client                               XHR                               Server




                                      26
Use Case: GWT RPCs

 Goals:
 – Serialization code begone
 – RPCs like they’re meant to be - interface
   methods
 – Make it fast to boot
Use Case: GWT RPCs


You write code that looks like this:


@RemoteServiceRelativePath("suggest")
public interface SuggestService extends RemoteService {
  String getSuggestions(String str) throws IllegalArgumentException;
}




                                   28
Behind the scenes

• Eclipse plugin creates the scaffolding
• GWT’s RPC generator creates the
  implementation
Use Case #2: Creating UIs

 Goals:
  – Utilize common dev practices
  – Minimize boilerplate code interface methods
  – Remove a few other frustrations along the way
Use Case #2: Creating UIs

You write code that looks like this:

<ui:UiBinder
  xmlns:ui="urn:ui:com.google.gwt.uibinder"
  xmlns:g="urn:import:com.google.gwt.user.client.ui">
	
  <ui:style>
    .contactsViewButtonHPanel {
      margin: 5px 0px 0x 5px;	
    }
  </ui:style>

  <g:HorizontalPanel addStyleNames="{style.contactsViewButtonHPanel}">
    <g:Button ui:field="addButton">Add</g:Button>
    <g:Button ui:field="deleteButton">Delete</g:Button>
  </g:HorizontalPanel>

</ui:UiBinder>




                                          31
Behind the scenes

• Eclipse plugin creates the scaffolding
• GWT’s UiBinder creates the implementation
From Eclipse to your browser




Your code...                                        Generators   Translators   Linkers
package com.google.gwt.samples.eclipsecon.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;

@SuppressWarnings("unused")
public class EclipseCon implements EntryPoint {

    public void onModuleLoad() {
      Window.alert("foo");
    }
}




                                                         33
Java




       Javascript
From Eclipse to your browser




Your code...                                        Generators   Translators   Linkers
package com.google.gwt.samples.eclipsecon.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;

@SuppressWarnings("unused")
public class EclipseCon implements EntryPoint {

    public void onModuleLoad() {
      Window.alert("foo");
    }
}




                                                         35
Entry points for your GWT app
                       36
GWT Tips and Tricks




  -gen will output a copy of the
       generated classes



                      37
GWT Tips and Tricks

 Reduce optimizations, reduce compile
 time
  – -draftCompile
    • Skip all optimizations
    • Development only


 Why worry about compiling at all?
A more efficient SDLC




Refresh        Does it work?   Yes   Compile

                    No


          Develop                    Deploy
We’re happy




40
But what about
 our users?




                 41
Optimize for the user

• Bundle resources
• Split code
Resource Bundling


• Example - associating icons with a contact
Resource Bundling


One at a time
Image image = new Image("images/image1.gif");
image.setHeight("50px");
image.setWidth("50px");
imagesPanel.add(image);
Resource Bundling

Initial download


Call to display images
Resource Bundling


All at once
public interface Resources extends ClientBundle {
  public static final Resources INSTANCE = GWT.create(Resources.class);

    @Source("Contacts.css")
    public ContactsCss contactsCss();

    @Source("images0.gif")
    public ImageResource image0();

    @Source("images1.gif")
    public ImageResource image1();

    ...

}
Resource Bundling


Initial download


Call to display images
Code splitting


Initial download



            Not needed on startup
Code splitting


Split points - runAsync()
@UiHandler("showImagesButton")
void onOkClicked(ClickEvent event) {
  GWT.runAsync(new RunAsyncCallback() {
    public void onSuccess() {
      showImagesDialog();
    }
  });
}
Code splitting


Initial download


Call to display images
Real world results - Google Wave
                                           1500
                                                                                                                 1400 KB
Size of Initial JavaScript Download (KB)




                                           1125




                                            750

                                                                             7x Decrease In
                                                                             Initial Download Size

                                            375




                                                                                                                 200 KB


                                              0
                                                  26-Nov   29-Apr   18-Jun   28-Jul   12-Sep   27-Oct   24-Dec   16-Mar
Creating GWT apps - “Direct” approach


Write a bunch of widgets with self-contained
logic
Problems:
 •   Hard to test - need GWTTestCase
 •   Mocks not encouraged - harder to write smaller tests
 •   Platform specific UI code - limits code reuse
 •   Too many dependencies - difficult to optimize
Creating GWT apps - MVP approach

 Cast:
  – Model - DTOs, and business logic
  – View - The display
  – Presenter - Application logic


 Goals:
  – Be practical
  – Avoid rigid patterns
  – Put the complex logic in your Presenters
  – Put the widget code in your Views
MVC
MVP
The MVP Relationship




         Model         Presenter   View
The MVP Relationship




         Model         Presenter   View
The MVP Relationship




         Model   History   Presenter   View
The MVP Relationship
  RPC




         Model   History   Presenter   View
The MVP Relationship

        Cache
  RPC




                Model   History   Presenter   View
The MVP Relationship

        Cache
  RPC




                Model   History      Presenter   View




                         Event Bus
The MVP Relationship

        Cache
  RPC




                Model   History      Presenter   View




                         Event Bus
The MVP Relationship

        Cache
  RPC




                Model   History      Presenter   View




                         Event Bus
Technology Interop

• Seam
• JSF
• G4JSF
Making the Cloud a Reality
Making the Cloud a Reality


     Email
Making the Cloud a Reality


      Email

Calendar
Making the Cloud a Reality


              Contacts
      Email

Calendar
Making the Cloud a Reality


              Contacts
      Email
                   Sales Force Automation
Calendar
Making the Cloud a Reality


              Contacts
      Email
                   Sales Force Automation
Calendar



                             Collaboration
Making the Cloud a Reality


              Contacts
      Email
                   Sales Force Automation
Calendar



  Banking                    Collaboration
Making the Cloud a Reality


               Contacts
      Email
                    Sales Force Automation
Calendar



  Banking                    Collaboration
              Social Network
Making the Cloud a Reality


               Contacts
      Email
                    Sales Force Automation
Calendar

      Your next big idea
  Banking                    Collaboration
              Social Network
The right languages and tools
            are key
</presentation>


Chris Ramsdale
cramsdale@google.com
Twitter: @cramsdale
http://googlewebtoolkit.blogspot.com/
JBoss World 2010

More Related Content

What's hot

Qt for Python
Qt for PythonQt for Python
Qt for PythonICS
 
iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術Yuji Hato
 
A friend in need - A JS indeed
A friend in need - A JS indeedA friend in need - A JS indeed
A friend in need - A JS indeedYonatan Levin
 
Qt and QML performance tips & tricks for Qt 4.7
Qt and QML performance tips & tricks for Qt 4.7Qt and QML performance tips & tricks for Qt 4.7
Qt and QML performance tips & tricks for Qt 4.7Pasi Kellokoski
 
An Introduction to CMake
An Introduction to CMakeAn Introduction to CMake
An Introduction to CMakeICS
 
Qt Internationalization
Qt InternationalizationQt Internationalization
Qt InternationalizationICS
 
[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt
[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt
[Webinar] QtSerialBus: Using Modbus and CAN bus with QtICS
 
Best Practices in Qt Quick/QML - Part 2
Best Practices in Qt Quick/QML - Part 2Best Practices in Qt Quick/QML - Part 2
Best Practices in Qt Quick/QML - Part 2Janel Heilbrunn
 
Lockless Producer Consumer Threads: Asynchronous Communications Made Easy
Lockless Producer Consumer Threads: Asynchronous Communications Made EasyLockless Producer Consumer Threads: Asynchronous Communications Made Easy
Lockless Producer Consumer Threads: Asynchronous Communications Made EasyICS
 
Apache Aries Overview
Apache Aries   OverviewApache Aries   Overview
Apache Aries OverviewIan Robinson
 
GR8Conf 2009: Industrial Strength Groovy by Paul King
GR8Conf 2009: Industrial Strength Groovy by Paul KingGR8Conf 2009: Industrial Strength Groovy by Paul King
GR8Conf 2009: Industrial Strength Groovy by Paul KingGR8Conf
 
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012Rajmahendra Hegde
 
Proxy Deep Dive JUG Saxony Day 2015-10-02
Proxy Deep Dive JUG Saxony Day 2015-10-02Proxy Deep Dive JUG Saxony Day 2015-10-02
Proxy Deep Dive JUG Saxony Day 2015-10-02Sven Ruppert
 
Meet the Widgets: Another Way to Implement UI
Meet the Widgets: Another Way to Implement UIMeet the Widgets: Another Way to Implement UI
Meet the Widgets: Another Way to Implement UIICS
 
GQuery a jQuery clone for Gwt, RivieraDev 2011
GQuery a jQuery clone for Gwt, RivieraDev 2011GQuery a jQuery clone for Gwt, RivieraDev 2011
GQuery a jQuery clone for Gwt, RivieraDev 2011Manuel Carrasco Moñino
 
Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...
Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...
Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...mfrancis
 
Gradle - time for another build
Gradle - time for another buildGradle - time for another build
Gradle - time for another buildIgor Khotin
 
Grails EE
Grails EEGrails EE
Grails EEGR8Conf
 

What's hot (20)

Qt for Python
Qt for PythonQt for Python
Qt for Python
 
GWT Extreme!
GWT Extreme!GWT Extreme!
GWT Extreme!
 
iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術
 
A friend in need - A JS indeed
A friend in need - A JS indeedA friend in need - A JS indeed
A friend in need - A JS indeed
 
Qt and QML performance tips & tricks for Qt 4.7
Qt and QML performance tips & tricks for Qt 4.7Qt and QML performance tips & tricks for Qt 4.7
Qt and QML performance tips & tricks for Qt 4.7
 
An Introduction to CMake
An Introduction to CMakeAn Introduction to CMake
An Introduction to CMake
 
Qt Internationalization
Qt InternationalizationQt Internationalization
Qt Internationalization
 
[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt
[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt
[Webinar] QtSerialBus: Using Modbus and CAN bus with Qt
 
Best Practices in Qt Quick/QML - Part 2
Best Practices in Qt Quick/QML - Part 2Best Practices in Qt Quick/QML - Part 2
Best Practices in Qt Quick/QML - Part 2
 
Lockless Producer Consumer Threads: Asynchronous Communications Made Easy
Lockless Producer Consumer Threads: Asynchronous Communications Made EasyLockless Producer Consumer Threads: Asynchronous Communications Made Easy
Lockless Producer Consumer Threads: Asynchronous Communications Made Easy
 
Apache Aries Overview
Apache Aries   OverviewApache Aries   Overview
Apache Aries Overview
 
GR8Conf 2009: Industrial Strength Groovy by Paul King
GR8Conf 2009: Industrial Strength Groovy by Paul KingGR8Conf 2009: Industrial Strength Groovy by Paul King
GR8Conf 2009: Industrial Strength Groovy by Paul King
 
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
 
Proxy Deep Dive JUG Saxony Day 2015-10-02
Proxy Deep Dive JUG Saxony Day 2015-10-02Proxy Deep Dive JUG Saxony Day 2015-10-02
Proxy Deep Dive JUG Saxony Day 2015-10-02
 
Meet the Widgets: Another Way to Implement UI
Meet the Widgets: Another Way to Implement UIMeet the Widgets: Another Way to Implement UI
Meet the Widgets: Another Way to Implement UI
 
GQuery a jQuery clone for Gwt, RivieraDev 2011
GQuery a jQuery clone for Gwt, RivieraDev 2011GQuery a jQuery clone for Gwt, RivieraDev 2011
GQuery a jQuery clone for Gwt, RivieraDev 2011
 
Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...
Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...
Automatically Managing Service Dependencies in an OSGi Environment - Marcel O...
 
Power ai image-pipeline
Power ai image-pipelinePower ai image-pipeline
Power ai image-pipeline
 
Gradle - time for another build
Gradle - time for another buildGradle - time for another build
Gradle - time for another build
 
Grails EE
Grails EEGrails EE
Grails EE
 

Viewers also liked

Jennifer shirley senior project photoshow final
Jennifer shirley senior project photoshow finalJennifer shirley senior project photoshow final
Jennifer shirley senior project photoshow finalJenniferShirley13
 
How to build rich web apps with GWT
How to build rich web apps with GWTHow to build rich web apps with GWT
How to build rich web apps with GWTChris Ramsdale
 
Main Task Power Point
Main Task Power PointMain Task Power Point
Main Task Power PointHAL09
 
Jennifer shirley senior project ppt final
Jennifer shirley senior project ppt finalJennifer shirley senior project ppt final
Jennifer shirley senior project ppt finalJenniferShirley13
 
Jennifer shirley literature as a therapy tool (research paper)
Jennifer shirley literature as a therapy tool (research paper)Jennifer shirley literature as a therapy tool (research paper)
Jennifer shirley literature as a therapy tool (research paper)JenniferShirley13
 

Viewers also liked (7)

Jennifer shirley senior project photoshow final
Jennifer shirley senior project photoshow finalJennifer shirley senior project photoshow final
Jennifer shirley senior project photoshow final
 
How to build rich web apps with GWT
How to build rich web apps with GWTHow to build rich web apps with GWT
How to build rich web apps with GWT
 
Portfolio
PortfolioPortfolio
Portfolio
 
Main Task Power Point
Main Task Power PointMain Task Power Point
Main Task Power Point
 
About Me
About MeAbout Me
About Me
 
Jennifer shirley senior project ppt final
Jennifer shirley senior project ppt finalJennifer shirley senior project ppt final
Jennifer shirley senior project ppt final
 
Jennifer shirley literature as a therapy tool (research paper)
Jennifer shirley literature as a therapy tool (research paper)Jennifer shirley literature as a therapy tool (research paper)
Jennifer shirley literature as a therapy tool (research paper)
 

Similar to JBoss World 2010

Hybrid Apps (Native + Web) using WebKit
Hybrid Apps (Native + Web) using WebKitHybrid Apps (Native + Web) using WebKit
Hybrid Apps (Native + Web) using WebKitAriya Hidayat
 
Hybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitHybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitAriya Hidayat
 
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13Fred Sauer
 
Cannibalising The Google App Engine
Cannibalising The  Google  App  EngineCannibalising The  Google  App  Engine
Cannibalising The Google App Enginecatherinewall
 
Devfest09 Cschalk Gwt
Devfest09 Cschalk GwtDevfest09 Cschalk Gwt
Devfest09 Cschalk GwtChris Schalk
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinMarvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinJava User Group Latvia
 
Google Dev Day2007
Google Dev Day2007Google Dev Day2007
Google Dev Day2007lucclaes
 
Introduction to Google Web Toolkit
Introduction to Google Web ToolkitIntroduction to Google Web Toolkit
Introduction to Google Web ToolkitDidier Girard
 
Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...
Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...
Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...DataStax Academy
 
Javascript as a target language - GWT KickOff - Part 2/2
Javascript as a target language - GWT KickOff - Part 2/2Javascript as a target language - GWT KickOff - Part 2/2
Javascript as a target language - GWT KickOff - Part 2/2JooinK
 
Introduction to QtWebKit
Introduction to QtWebKitIntroduction to QtWebKit
Introduction to QtWebKitAriya Hidayat
 
Native Java with GraalVM
Native Java with GraalVMNative Java with GraalVM
Native Java with GraalVMSylvain Wallez
 
Testing of javacript
Testing of javacriptTesting of javacript
Testing of javacriptLei Kang
 
How to lock a Python in a cage? Managing Python environment inside an R project
How to lock a Python in a cage?  Managing Python environment inside an R projectHow to lock a Python in a cage?  Managing Python environment inside an R project
How to lock a Python in a cage? Managing Python environment inside an R projectWLOG Solutions
 
DIY: Computer Vision with GWT.
DIY: Computer Vision with GWT.DIY: Computer Vision with GWT.
DIY: Computer Vision with GWT.JooinK
 
DIY- computer vision with GWT
DIY- computer vision with GWTDIY- computer vision with GWT
DIY- computer vision with GWTFrancesca Tosi
 
Microservices DevOps on Google Cloud Platform
Microservices DevOps on Google Cloud PlatformMicroservices DevOps on Google Cloud Platform
Microservices DevOps on Google Cloud PlatformSunnyvale
 
Docker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline ExecutionDocker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline ExecutionBrennan Saeta
 
Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy
Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy
Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy Jeffrey Holden
 

Similar to JBoss World 2010 (20)

Hybrid Apps (Native + Web) using WebKit
Hybrid Apps (Native + Web) using WebKitHybrid Apps (Native + Web) using WebKit
Hybrid Apps (Native + Web) using WebKit
 
Hybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitHybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKit
 
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13
SF JUG - GWT Can Help You Create Amazing Apps - 2009-10-13
 
Cannibalising The Google App Engine
Cannibalising The  Google  App  EngineCannibalising The  Google  App  Engine
Cannibalising The Google App Engine
 
Devfest09 Cschalk Gwt
Devfest09 Cschalk GwtDevfest09 Cschalk Gwt
Devfest09 Cschalk Gwt
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinMarvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
 
Google Dev Day2007
Google Dev Day2007Google Dev Day2007
Google Dev Day2007
 
Introduction to Google Web Toolkit
Introduction to Google Web ToolkitIntroduction to Google Web Toolkit
Introduction to Google Web Toolkit
 
Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...
Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...
Cassandra Summit 2014: Highly Scalable Web Application in the Cloud with Cass...
 
Javascript as a target language - GWT KickOff - Part 2/2
Javascript as a target language - GWT KickOff - Part 2/2Javascript as a target language - GWT KickOff - Part 2/2
Javascript as a target language - GWT KickOff - Part 2/2
 
Introduction to QtWebKit
Introduction to QtWebKitIntroduction to QtWebKit
Introduction to QtWebKit
 
Native Java with GraalVM
Native Java with GraalVMNative Java with GraalVM
Native Java with GraalVM
 
Testing of javacript
Testing of javacriptTesting of javacript
Testing of javacript
 
How to lock a Python in a cage? Managing Python environment inside an R project
How to lock a Python in a cage?  Managing Python environment inside an R projectHow to lock a Python in a cage?  Managing Python environment inside an R project
How to lock a Python in a cage? Managing Python environment inside an R project
 
DIY: Computer Vision with GWT.
DIY: Computer Vision with GWT.DIY: Computer Vision with GWT.
DIY: Computer Vision with GWT.
 
DIY- computer vision with GWT
DIY- computer vision with GWTDIY- computer vision with GWT
DIY- computer vision with GWT
 
Microservices DevOps on Google Cloud Platform
Microservices DevOps on Google Cloud PlatformMicroservices DevOps on Google Cloud Platform
Microservices DevOps on Google Cloud Platform
 
Docker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline ExecutionDocker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline Execution
 
GWT MVP Case Study
GWT MVP Case StudyGWT MVP Case Study
GWT MVP Case Study
 
Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy
Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy
Deploying Cloud Native Red Team Infrastructure with Kubernetes, Istio and Envoy
 

JBoss World 2010

  • 1.
  • 2. Google Web Toolkit 0-60 in no time flat Chris Ramsdale Developer Relations, Google new java.util.Date();
  • 3. Agenda • Overview • Demo • Deeper dive • Optimizations for developers • Optimizations for users • Intro to GWT & MVP
  • 4. From 25,000 feet • Development toolkit, not a framework • Code in Java, run as Javascript • One codebase, any browser • Makes Ajax a piece of cake...and faster • Used within many Google products, including Google Wave and Ad Words
  • 5. The GWT Family GWT SDK Google Plugin Speed Tracer For Eclipse
  • 6. It’s a loving relationship
  • 7. It’s a loving relationship Develop
  • 8. It’s a loving relationship Develop Debug
  • 9. It’s a loving relationship Develop Debug Optimize
  • 10. Focus on the users Our users - developers – Leverage existing IDEs and tools – Minimize refresh time between codes changes – Automate where possible Your users - customers – Minimize startup time – Make it a comfortable experience – Allow them to select the browse
  • 12. Different Goals Developers • Next killer feature • Making it look good • Code refactoring
  • 13. Different Goals Developers Customers • Next killer feature • Make it fast • Making it look good • ...oh, and don’t charge my credit card twice • Code refactoring
  • 14. Different Goals Developers Customers • Next killer feature • Make it fast • Making it look good • ...oh, and don’t charge my credit card twice • Code refactoring
  • 15. No plugins required Silverlight VML Flash
  • 16. Nothing against them, but... Foo Player not available for your device We restrict use of technologies required by products like Foo Player...
  • 17. Quirkiness Firefox Webkit (Safari) Opera IE Typical portable 2876 ms 1276 ms 2053 ms 4078 ms setInnerText() textContent=... - 908 ms 1386 ms - innerText=... 2477 ms 918 ms 1520 ms 2469 ms DOM manipulation 7148 ms 1997 ms 4836 ms 14800 ms
  • 18. Quirkiness Firefox Webkit (Safari) Opera IE Typical portable 2876 ms 1276 ms 2053 ms 4078 ms setInnerText() textContent=... - 908 ms 1386 ms - innerText=... 2477 ms 918 ms 1520 ms 2469 ms DOM manipulation 7148 ms 1997 ms 4836 ms 14800 ms Improvement 14% 29% 32% 39%
  • 19. Quirkiness Firefox Webkit (Safari) Opera IE Typical portable 2876 ms 1276 ms 2053 ms 4078 ms setInnerText() textContent=... - 908 ms 1386 ms - innerText=... 2477 ms 918 ms 1520 ms 2469 ms DOM manipulation 7148 ms 1997 ms 4836 ms 14800 ms Improvement 14% 29% 32% 39% http://quirksmode.org/blog/
  • 20. Can you find the bug? function getMax(values) { var maximum = values[0]; for (var i = 0; i < values.length; ++i) { if (values[i] > maximum) { maxinum = values[i]; } } return maximum; }
  • 21. Can you find the bug? function getMax(values) { var maximum = values[0]; for (var i = 0; i < values.length; ++i) { if (values[i] > maximum) { maxinum = values[i]; } } return maximum; } Hint: Javascript is a dynamic language
  • 22. ...GWT Can Java is a static language
  • 23. Demo time To see this in action, letʼs: • Create a new web app • Debug within the browser • Update, refresh, run • Deploy to JBoss Enterprise Application Platform 14
  • 24. A Java to Javascript compiler right?
  • 25. More like assembly-level JS function rd(a,b){var c;if(b.b){b.b=false;b.c=null}c=b.c;b.c=a.f;try{++a.c;Cd(a.e,b,a.d)}finally{-- a.c;a.c==0&&sd(a)}if(c==null){b.b=true;b.c=null}else{b.c=c}} function Hb(b,c){yb();$wnd.setTimeout(function(){var a=$entry(Eb)(b);a&&$wnd.setTimeout(arguments.callee,c)},c)} function ih(a,b){var c,d;if(b.e!=a){return false}try{Vg(b,null)}finally{c=b.f;(d=(hc(),c).parentNode,(!d||d.nodeType! =1)&&(d=null),d).removeChild(c);ki(a.b,b)}return true} function hj(c){if(c.length==0||c[0]>ao&&c[c.length-1]>ao){return c}var a=c.replace(/^(s*)/,cn);var b=a.replace(/s* $/,cn);return b} function qj(a){var b,c,d,e;b=0;d=a.length;e=d-4;c=0;while(c<e){b=a.charCodeAt(c+3)+31*(a.charCodeAt(c +2)+31*(a.charCodeAt(c+1)+31*(a.charCodeAt(c)+31*b)))|0;c+=4}while(c<d){b=b*31+a.charCodeAt(c++)}return b|0} function Bj(a){var b,c,d,e;e=this.v();a.length<e&&(a=Pd(a,e));d=a;c=this.p();for(b=0;b<e;++b) {Ud(d,b,c.s())}a.length>e&&Ud(a,e,null);return a} function xl(a){var b,c,d;a.length<this.c&&(a=(c=a,d=Qd(0,this.c),Sd(c.aC,c.tI,c.qI,d),d));for(b=0;b<this.c;++b) {Ud(a,b,this.b[b])}a.length>this.c&&Ud(a,this.c,null);return a} function Ud(a,b,c){if(c!=null){if(a.qI>0&&!ee(c.tI,a.qI)){throw wi(new ui)}if(a.qI<0&&(c.tM==gm||c.tI==2)){throw wi(new ui)}}return a[b]=c} function hi(a,b,c){var d,e;if(c<0||c>a.c){throw Qi(new Oi)}if(a.c==a.b.length){e=Rd(we, 47,8,a.b.length*2,0);for(d=0;d<a.b.length;++d){Ud(e,d,a.b[d])}a.b=e}++a.c;for(d=a.c-1;d>c;--d) {Ud(a.b,d,a.b[d-1])}Ud(a.b,c,b)} function th(b,c){var i;rh();var a,e,f,g,h;e=null;for(h=b.p();h.r();){g=fe(h.s(),8);try{c.q(g)}catch(a) {a=Ge(a);if(ie(a,11)){f=a;!e&&(e=Fl(new Dl));i=Sj(e.b,f,e)}else throw a}}if(e){throw sh(new oh,e)}} function Tj(j,a,b,c){var d=j.b[c];if(d){for(var e=0,f=d.length;e<f;++e){var g=d[e];var h=g.y();if(j.x(a,h)){var i=g.z();g.A(b);return i}}}else{d=j.b[c]=[]}var g=Ul(new Sl,a,b);d.push(g);++j.e;return null} function bk(a){var b,c,d;if((a==null?null:a)===this){return true}if(!(a!=null&&de(a.tI,18))){return false}c=fe(a, 18);if(c.v()!=this.v()){return false}for(b=c.p();b.r();){d=b.s();if(!this.u(d)){return false}}return true} function Cd(a,b,c){var d,e,f,g,h,i,j;g=b.j();d=(h=fe(Nj(a.b,g),4),!h?0:h.c);if(c){for(f=d-1;f>=0;--f) {e=(i=fe(Nj(a.b,g),4),fe((Nk(f,i.c),i.b[f]),13));b.i(e)}}else{for(f=0;f<d;++f){e=(j=fe(Nj(a.b,g), 4),fe((Nk(f,j.c),j.b[f]),13));b.i(e)}}} function Fb(a){var b,c,d,e,f,g;b=false;d=a.length;f=(new Date).getTime();while((new Date).getTime()-f<100) {for(c=0;c<d;++c){g=a[c];if(!g){continue}if(!g[0].h()){a[c]=null;b=true}}}if(b){e=[];for(c=0;c<d;++c){if(!a[c]) {continue}e[e.length]=a[c]}return e}else{return a}} function cf() {cf=gm;af={};bf=[];af[pn]=[lg,kg,mg];af[qn]=[tg,sg];af[rn]=[xg,wg];af[sn]=[Bg,Ag,Cg];_e(bf,se,pn);_e(bf,te,qn);_e(bf,u e,rn);_e(bf,ve,sn)} 16
  • 26. I can haz pretty print plz Simply adjust the output style of the compiler via the plugin 17
  • 27. Yes, a Java to Javascript compiler function init(){ $GreetingService_Proxy(new GreetingService_Proxy); $add_0(($clinit_99() , get_0(null)), $Label(new Label, 'Foo')); $wnd.alert('foo'); } function caught_0(e){ if (e != null && canCast(e.typeId$, 11)) { return e; } return $JavaScriptException(new JavaScriptException, e); } function $RemoteServiceProxy(this$static){ return this$static; } function RemoteServiceProxy(){ } 18
  • 30. From Eclipse to your browser Eclipse Browser Plugins package com.google.gwt.samples.eclipsecon.client; TCP import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; @SuppressWarnings("unused") Code public class EclipseCon implements EntryPoint { Server public void onModuleLoad() { Window.alert("foo"); } HTTP } Jetty Server 21
  • 31. From Eclipse to deployment Your code... Generators Translators Linkers package com.google.gwt.samples.eclipsecon.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; @SuppressWarnings("unused") public class EclipseCon implements EntryPoint { public void onModuleLoad() { Window.alert("foo"); } } 22
  • 32. From Eclipse to deployment Your code... Generators Translators Linkers package com.google.gwt.samples.eclipsecon.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; @SuppressWarnings("unused") public class EclipseCon implements EntryPoint { public void onModuleLoad() { Window.alert("foo"); } } 23
  • 33. Provide the power behind your GWT app Automate away boilerplate code Foundation for permutations 24
  • 34. Use Case: GWT RPCs Typical Ajax call Serialization Serialization Code Code Client XHR Server 25
  • 35. Use Case: GWT RPCs GWT-enabled Ajax Serialization Serialization GWT Proxy GWT Proxy Code Code Client XHR Server 26
  • 36. Use Case: GWT RPCs Goals: – Serialization code begone – RPCs like they’re meant to be - interface methods – Make it fast to boot
  • 37. Use Case: GWT RPCs You write code that looks like this: @RemoteServiceRelativePath("suggest") public interface SuggestService extends RemoteService { String getSuggestions(String str) throws IllegalArgumentException; } 28
  • 38. Behind the scenes • Eclipse plugin creates the scaffolding • GWT’s RPC generator creates the implementation
  • 39. Use Case #2: Creating UIs Goals: – Utilize common dev practices – Minimize boilerplate code interface methods – Remove a few other frustrations along the way
  • 40. Use Case #2: Creating UIs You write code that looks like this: <ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"> <ui:style> .contactsViewButtonHPanel { margin: 5px 0px 0x 5px; } </ui:style> <g:HorizontalPanel addStyleNames="{style.contactsViewButtonHPanel}"> <g:Button ui:field="addButton">Add</g:Button> <g:Button ui:field="deleteButton">Delete</g:Button> </g:HorizontalPanel> </ui:UiBinder> 31
  • 41. Behind the scenes • Eclipse plugin creates the scaffolding • GWT’s UiBinder creates the implementation
  • 42. From Eclipse to your browser Your code... Generators Translators Linkers package com.google.gwt.samples.eclipsecon.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; @SuppressWarnings("unused") public class EclipseCon implements EntryPoint { public void onModuleLoad() { Window.alert("foo"); } } 33
  • 43. Java Javascript
  • 44. From Eclipse to your browser Your code... Generators Translators Linkers package com.google.gwt.samples.eclipsecon.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; @SuppressWarnings("unused") public class EclipseCon implements EntryPoint { public void onModuleLoad() { Window.alert("foo"); } } 35
  • 45. Entry points for your GWT app 36
  • 46. GWT Tips and Tricks -gen will output a copy of the generated classes 37
  • 47. GWT Tips and Tricks Reduce optimizations, reduce compile time – -draftCompile • Skip all optimizations • Development only Why worry about compiling at all?
  • 48. A more efficient SDLC Refresh Does it work? Yes Compile No Develop Deploy
  • 50. But what about our users? 41
  • 51. Optimize for the user • Bundle resources • Split code
  • 52. Resource Bundling • Example - associating icons with a contact
  • 53. Resource Bundling One at a time Image image = new Image("images/image1.gif"); image.setHeight("50px"); image.setWidth("50px"); imagesPanel.add(image);
  • 55. Resource Bundling All at once public interface Resources extends ClientBundle { public static final Resources INSTANCE = GWT.create(Resources.class); @Source("Contacts.css") public ContactsCss contactsCss(); @Source("images0.gif") public ImageResource image0(); @Source("images1.gif") public ImageResource image1(); ... }
  • 57. Code splitting Initial download Not needed on startup
  • 58. Code splitting Split points - runAsync() @UiHandler("showImagesButton") void onOkClicked(ClickEvent event) { GWT.runAsync(new RunAsyncCallback() { public void onSuccess() { showImagesDialog(); } }); }
  • 60. Real world results - Google Wave 1500 1400 KB Size of Initial JavaScript Download (KB) 1125 750 7x Decrease In Initial Download Size 375 200 KB 0 26-Nov 29-Apr 18-Jun 28-Jul 12-Sep 27-Oct 24-Dec 16-Mar
  • 61. Creating GWT apps - “Direct” approach Write a bunch of widgets with self-contained logic Problems: • Hard to test - need GWTTestCase • Mocks not encouraged - harder to write smaller tests • Platform specific UI code - limits code reuse • Too many dependencies - difficult to optimize
  • 62.
  • 63. Creating GWT apps - MVP approach Cast: – Model - DTOs, and business logic – View - The display – Presenter - Application logic Goals: – Be practical – Avoid rigid patterns – Put the complex logic in your Presenters – Put the widget code in your Views
  • 64. MVC
  • 65. MVP
  • 66. The MVP Relationship Model Presenter View
  • 67. The MVP Relationship Model Presenter View
  • 68. The MVP Relationship Model History Presenter View
  • 69. The MVP Relationship RPC Model History Presenter View
  • 70. The MVP Relationship Cache RPC Model History Presenter View
  • 71. The MVP Relationship Cache RPC Model History Presenter View Event Bus
  • 72. The MVP Relationship Cache RPC Model History Presenter View Event Bus
  • 73. The MVP Relationship Cache RPC Model History Presenter View Event Bus
  • 75. Making the Cloud a Reality
  • 76. Making the Cloud a Reality Email
  • 77. Making the Cloud a Reality Email Calendar
  • 78. Making the Cloud a Reality Contacts Email Calendar
  • 79. Making the Cloud a Reality Contacts Email Sales Force Automation Calendar
  • 80. Making the Cloud a Reality Contacts Email Sales Force Automation Calendar Collaboration
  • 81. Making the Cloud a Reality Contacts Email Sales Force Automation Calendar Banking Collaboration
  • 82. Making the Cloud a Reality Contacts Email Sales Force Automation Calendar Banking Collaboration Social Network
  • 83. Making the Cloud a Reality Contacts Email Sales Force Automation Calendar Your next big idea Banking Collaboration Social Network
  • 84.
  • 85. The right languages and tools are key

Editor's Notes