About
fv_2007
Agile innovative developer with deep insight into lots of platforms, technologies and protocols. Absolute “early adopter” in Web 2.0 technologies and more. Large professional network and eagerly talking about architecture, strategy, design patterns, restful ressources, object-oriented thinking and modeling languages such as PML. Special interest in programminglanguages constructs, knowledge on languages like Smalltalk, Erlang, Java, Clojure, Scala, Ruby... read more
Comments
Language

GWT and The Programming Model March 17, 2010 19:26 about 1 year ago

Programeringsmodellen i GWT er stateful! Denne ene sætning er nok den som kræver mest tilvænning for dem der vil lave gode applikationer med Googe GWT. Featuren er den mest misforståede blandt tidligere serverside programmører. Jeg siger det lidt nedladende fordi jeg mener at serverside folk skulle være smidt ud forlængst og de vil aldrig forstå eller kode GWT UI på en stengent måde. GWT handler om at skrive UI kode over en broken web og din programmeringsmodel er tilstandfuld!

Når man i gamle dage konstruerede en JSP side var programmeringsmodellen stateless. Hvis en værdi skulle overføres fra en side til en anden kunne man bruge en server session og dermed fik man state på serveren. Den næste side slog session op og fandt værdien. Forudsat naturligvis at man stadig var på den samme server eller der var en slags replikering mellem dem.

Med GWT er dette koncept væk. Al den kode man skriver til GWT compileren skal eksekvere helt ude på klienten i form af Javascript. Og hvad der er endnu vigtigere er at enhver klients tilstand ligger hos klienten selv. Det betyder blandt andet at der ikke længere er brug for serverstate og dermed kan løses en masse problemer som performents og skalerbarhed.

Desværre er det sådan at de gamle serverside programmører er bange for at stole på Javascript og tror ikke det virker. Derfor bruges sessionen stadig med alle de implikationer det medføre som serverstate og flere lag af tilstand tilknytte klienten. Flere lag af tilstand er første indikation på at applikationen aldring kommer til at fungere efter hensigten.

Hvad vil det sige at man sidder i en tilstandsfuld programmeringsmodel og hvad betyder det for den mentale tilstand?

En tilstandløst programmeringsmodel er typisk karaktiseret ud fra request/response paradigme hvor det handler om at stille en værdi gennem uden at tænke på helheden. Efter trelags modellen betyder det er der kommer et isoleret request fra en facade og input værdierne parses og bruge til argument mod den underliggede database hvorefter et response konstrueres og returneres uden hensyntagen til datas livsformål eller andet.

En tilstandsfuldt programmeringsmodel er typisk karaktiseret udfra en given kontekst. Paradigme her er mere baseret om events hvor det handler om at vedligeholde en tilstand i en sammenhæng. Når et event invokeres af en bruger eller et system opstår et tilstands skift med tydelig hensyntagen til værdiers livsforløb og kontekst. En lampe kan være slukket eller tændt. Hvis den er tændt og man prøver at tænde den igen skal tilstanden kunne håndtere indempotens.

I det efterfølgende eksempel vil jeg forsøge at attakere de to omtalte problemer. Applikations tilstand og del/hersk kodeopdeling. Koden er bygget op omkring et MVP Design Pattern. Model View Presenter har i forbindelse med GWT en god, enkel og separerende funktion. Det bevirker at man kan bygge sine UI komponenter direkte samme men forretningen på en workshop mens den ofte mere problemfyldte databinding kan sker senere.

I GWT koden har jeg ikke benyttet Eventbus og ideen om at register sig, idet jeg mener det er et anti pattern. Grunden er Java’s manglen evne til at være event orienteret og det bryder jo direkte med GWT fremmeste strategi, at have tilstand. Hvis der findes både en kontekst og en tilstand burde det ikke være nødvendigt at sende en besked i en centraliseret eventbus for at fortælle komponenten ved siden af at værdien nu er 4. Det er en bird-watcher strategi og den føre kun til støj. Ligeledes bryder jeg mig ikke om centraler der styre og kontrollere alting.

Applikationen har en host webside som klienten henter. Deri findes statiske ting som HTML/CSS styles plus ref til en fil hvori det generede javascript findes. I denne javascript findes også evnen til at kunne lave AJAX kald over RPC til server delen af GWT. Host siden definere med HTML divs de områder hvori forskellige GWT moduler skal fungere. Et modul i GWT er defineret ved en *.gwt.xml fil, med et entry point der peger på klasse af typen EntryPoint.

public class MainEntryPoint implements EntryPoint {

    @Override
    public void onModuleLoad() {
        RootPanel.get("post").add(new PostView());
    }

}

Denne entry klasse skaber en instans af PostView og adderede den til HTML/DIV tag post i host siden. PostView er kun showoff og ren klientkode og viser en dropdown box og et par tekster.

public class PostView extends Composite {

  // the UI components
  VerticalPanel canvas = new VerticalPanel();
  Label postTitle = new Label();
  Label postBody = new Label();
  ListBox postTitleDropdownbox = new ListBox();

  // the constructor
  public PostView() {
    canvas.clear();
    postTitleDropdownbox.setName("Post titles");
    postTitle.setText("Post body");
    postBody.setText("Post paragraf");

    canvas.add(postTitleDropdownbox);
    canvas.add(postTitle);
    canvas.add(postBody);
    initWidget(canvas);
  }

Ca. 50% af denne kode vil jeg forvente at enhver udvikler med respekt for sig selv kan skriver under en workshop med forretningen som en slags prototyping. Det er vigtigt at forstå at et view er en partial og man inden denne workshop ved ca hvordan hovedlinjerne i applikationen ser ud. I mange tilfælde har jeg set forskellige former for nedarvning på en fremgangmåde så et view bliver blandet med funktionalitet og operationer.

Alle operationer og events styres af den anden del af MVP design pattern. En presenter bliver instanstieret af et view og i denne procent overførers view’et this reference af sig selv.

  private PostPresenter presenter = new PostPresenter(this);

Her skabes en instans af en presenter med argumentet this. På denne måde kender presenter’en til sig selv, til view’et og til modellen der repræsenter det bagvedliggen system eller funktionalitet. Men først RPC kommunikation.

GWT-RPC kommunikationen består af tre interfaces/classes der specificere protocollen mellem klient klasserne der gør det muligt at sterilisere data over linjen mellem GWT klient og GWT serverside. GWT RPC kan sende alle objekter over linjen hvis de kan implementere GWT IsSerializable eller Java Serializable interfacet og ikke har en grafdybet på mere end ca. fem “has a” relationer. Det er en lidt usikker grænse.

// client/server side
@RemoteServiceRelativePath("blogservice")
public interface PostService extends RemoteService {
  public List<Post> findAll();
}

// client side
public interface PostServiceAsync {
  public void findAll(AsyncCallback<List<Post>> asyncCallback);
}

// server side
public class PostServiceImpl extends RemoteServiceServlet implements PostService {
  @Override
  public List<Post> findAll() {
    return factory.findAll();
  }
}

PostService interfacet specificere selve kommunikations protokollen mellem client og server hvilket består af et metodekald der retunere en liste af posts. Den er påhæftet en annotation der beskriver server kontekst. PostServiceAsync (proxy) interfacet specificere den asynkrone del af kaldet og returnere void, men tager som argument et callback objekt der kalder tilbage til klient implementationen når den synkrone del af server kaldet i PostServiceImpl er færdigt. Server delen af RPC kaldet bliver sat op i web.xml. PostServiceImpl er en servlet og lever på serveren.

Her er den første rigtige interessante del af koden. PostServiceImpl arver i sidste ende fra HttpServlet og der findes dermed kun en instans mens der findes mange PostServiceAsync. Ved at implementere en single threaded model kan man sikre at kun er server tråd har adgang til denne ene instans af gange men ofte vil mange tråde løbe gennem samtidigt. Det vil også sige at alle tråde dele alle instans variabler og altså dermed tilstand. I traditionel Web1.0 miljø gemte man tilstand på sessionen. Servlet’en hentede bruger og tilstand ud af session hver gang en metode blev kaldt og det var serverens ansvar at kunne register hvornår hvilken. Med Web2.0 og GWT er det anderledes. Man skal se en service eller en servlet som en stateless service, i dette tilfælde med GWT en tilstandløs RPC service.

Hvor er tilstanden så? Lad os kigge på Presenter’en.

public class PostPresenter {

  // the ticket
  private Ticket ticket;

  // the service
  private PostServiceAsync service = (PostServiceAsync) GWT.create(PostService.class);

  // the view
  private final PostView view;

  // constructor
  public PostPresenter(PostView theView) {
    this.view = theView;
    this.ticket = pull som cookie or stuff
  }
...

Presenter’en er en POJO med private instanser af en stateless server. Tilstand der overføres fra RPC kaldet bliver indsat direkte i UI instanser men andre tilstands værdier blot kan sættes som private instanser som fx. Ticket. Selv hvis det ene vindue er en ekseakt klon af et andet vil det virker. GWT håndtere dette for dig under fronthjelmen. I mere komplekse applikationer hvor man har brug for at bære tilstande rundt gennem hele forløbet eller i opbrudte flows kan man benytte en AppController der kan bruges overalt inden for et gwt-modul. Samme komponent bruges til at disable history.back browser funktionalitet.

Vi befinder og i en stateful/tilstandsfuld programmeringsmodel og konteksten glæder hele dette modul. Hvis du behøver at dele tilstand mellem flere moduler kan man bruge native javascript. Lad os prøve at kalde RPC servicen og fylde noget data i UI’en.

  public void updateTitles() {
    AsyncCallback<List<Post>> callback = new AsyncCallback<List<Post>>() {
      @Override
      public void onFailure(Throwable caught) {
        Window.alert(caught.getMessage());
      }
      @Override
      public void onSuccess(List<Post> result) {
        for (Post p : result) {
          view.postTitleDropdownbox.addItem(p.getTitle());
        }
      }
    };
    service.findAll(callback);
  }

To ting er interessante. Først laver jeg en AsyncCallback inner klasse som service.findAll tager som argument. Det betyder at metoden returnere øjeblikkeligt mens RPC kommunikationen sættes i gang. Når serveren svare kaldes onSuccess og post værdierne bliver sat i dropbox’en. Dog er det værd at bemærke at state jo er hæftet på module og dermed forsvinder når host webside reloades.

Måske har du lagt mærke til at jeg kalder direkte view’es private instanser. Det er fordi this jo bliver overført når der skabes en presenter og det er super belejligt fordi view’et derved bliver instans i presenter’en. Jeg har set mange som konstruer setter metoder i view’et men det synes jeg er et anti pattern. Det kan dog være okey hvis man vil teste på en bestemt måde men som jeg ser det er det ikke nødvendigt at teste at man kan sætte en værdi på et objekt.

Der er flere ting jeg kan lide ved GWT men der også åbenlyse pitfalls. Den mentale barrierer er langt den støreste forhindring der skal overvindes. En programmør der ikke har konstrueret brugergrænseflade med swing eller komponent baseret applikationer kan ikke begå en velafvejet applikation med GWT! Den næste er at tranformere sig selv til en stateful programmeringsmodel og stole på at GWT kan tage vare på dit tilstand. Skab separation i din koden uden at danne “class explosion” overalt. Flere af de projekter jeg har reviewed har helt misforstået arv og genbrug i relation til GWT og widgets og med et kan en side kun vises hvis der findes en bestem type data.

To hoved ting at tage med i bussen.

  • Applikation tilstand sker på klienten, og ikke på serveren.
  • Applikation udsende og operationer på data skal separeres.

Check denne inden du kaster dig ud i det fordi du har travlt. Large scale application development and MVP

GWT er en abstraktion, den abstrahere event baseret Javascript væk fra programmøren og indføre i stedet Java der ikke er event baseret. Men GWT er nok det bedste hvis man ikke ikke kan skrive koden i de rigtige teknologere. Og her leaker abstraktionen fordi man faktisk kun kan lave gode applikationer hvis man har kompetencer i de ægte grundteknologer.


By Frank Vilhelmsen - 2 tags: java gwt - Add comment