Kommunikation in den Zeiten von AJAX

Server-Seite

Das Ziel ist die Realisierung der in Abbildung 3 skizzierten Lösung und zwar derart, dass BookSearcher auf dem Server in Java implementiert wird, während unsere View – BookSearcherDialog – auf dem Browser in JavaScript umgesetzt wird.

Abb. 3: Model View Presenter

Konzentrieren wir uns zunächst auf den Server. Im ersten Schritt definieren wir beide Schnittstellen als einfache Java-Interfaces:

import jayjax.IFacade;
public interface BookSearchPresenter extends IFacade {
  void search(String searchString);
}

import java.util.List;
public interface BookSearchView {
  void displayList(List resultList);
}

Die Verwendung des parametrisierten Parameters List resultList dient dazu, die noch nicht existierende Implementierung der Presenter-Schnittstelle ein wenig typsicherer zu machen. In Listing 1 fügen wir die Suchfunktionalität hinzu.

Listing 1
------------------------------------------------------------------------------------
import java.util.*;
import jayjax.AbstractAjaxFacade;
public class BookSearcher 
    extends AbstractAjaxFacade 
    implements BookSearchPresenter {
  public void init() {
    getView().displayList(new ArrayList());
  }
  public void search(String searchString) {
    List result = 
      calculateSearchResult(searchString);
    getView().displayList(result);
  }
  private List calculateSearchResult(
      String searchString) {
    List result = new ArrayList();
    // Retrieve list and add book objects to result
    ...
    return result;
  }
}

public class Book {
  private String title, author;
  /** Needed for deserialization */
  public Book() {}
  public Book(String title, String author) {
    this.title = title;
    this.author = author;	
  }
  public String getAuthor() {return author;}
  public String getTitle() {return title;}
}

Zu beachten ist, dass BookSearcher von jayjax.AbstractAJAXFacade abgeleitet wurde. Dies versetzt unser Framework in die Lage, den BookSearcher-Objekten den View-Proxy mitzugeben, auf den dann mittels getView() typsicher zugegriffen werden kann. JayJax kümmert sich selbstständig um die Weiterleitung aller Methodenaufrufe an den Client. Bei der Spezifikation der Schnittstellen gibt es eine Hand voll Einschränkungen:

  • Solange man die Presenter-Methoden in JavaScript mit einem einfachen presenter.method(par1, par2, …) aufrufen möchte, ist ein Überladen von Methoden nicht zulässig, da in JavaScript Methoden ausschließlich anhand ihres Namens unterschieden werden.
  • Methoden der View-Schnittstelle können keinen Rückgabewert (oder Exceptions) haben bzw. dieser Rückgabewert wird nie an den aufrufenden Servercode zurückgeliefert, da die Kommunikation vom Server zum Client ausschließlich asynchron abläuft. Tatsächlich stellt dies jedoch im MVP-Kontext keinen Nachteil dar, da sich der Presenter (hier BookSearcher) nicht darum kümmern sollte, wie die View denn tatsächlich mit den Update-Nachrichten umgeht.
  • Methodenaufrufe an den Presenter können Rückgabewerte liefern. Diese müssen jedoch JavaScript-typisch asynchron, d.h. in einer Callback-Methode verarbeitet werden – etwa so:
presenter.method(par1, par2, ... , {onSuccess: processResult});
function processResult(returnValue) {
  //do something with returnValue
}

Und nun können wir Interfaces und Implementierung über Java-Annotations miteinander verknüpfen und dabei auch dem Presenter einen Namen zuweisen (hier: searcher), unter dem auf diesen aus JavaScript heraus zugegriffen werden kann:

@AjaxFacade(name="searcher", 
  view=BookSearchView.class, 
  implementation=BookSearcher.class)
public interface BookSearchPresenter extends IFacade {...}

Falls die beiden JayJax Servlets und die Fassade korrekt in der web.xml registriert wurden – dies ist detailliert in der JayJax-Dokumentation beschrieben -, dann kümmert sich das Framework um folgende Dinge:

  • Es generiert „on the fly“ JavaScript-Code, der die Presenter-Implementierung als einfaches JavaScript-Objekt auf dem Client verfügbar macht.
  • Es generiert JavaScript-Code, der dafür sorgt, dass auf dem Client nichtimplementierte View-Methoden zu einer Fehlermeldung (als Alert-Box) führen.
  • Es transportiert alle Methodenaufrufe vom Client zum Server und zurück. Dabei kümmert es sich um die notwendige Serialisierung und Deserialisierung; ebenso ist es möglich, auf unterschiedliche Probleme bei der Kommunikation oder bei der serverseitigen Abarbeitung zu reagieren.

Gegenwärtig kommt JayJax mit allen primitiven Typen und den Standardklassen wie String, java.util.List und java.util.Date zurecht. Darüber hinaus lassen sich Objekte, die Javas Bean-Konvention entsprechen, ohne zusätzlichen Programmieraufwand über die Leitung schieben. Für Spezialfälle gibt es zusätzlich die Möglichkeit, eigene Serialisierungs- und Deserialisierungsmechanismen zu programmieren und ins Framework einzuhängen. Hinter den Kulissen arbeitet JayJax mit einem XML-basierten Serialisierungsformat; eine Umstellung auf JSON (JavaScript Object Notation), eine einfache JavaScript-basierte Möglichkeit, Objekte zu serialisieren, wäre jedoch mit wenig Aufwand möglich.

Auf dem Client

Um den Client MVP-tauglich zu machen, müssen lediglich einige JavaScript-Bibliotheken und generierte Code-Seiten eingebunden werden:

     

Die Dateiendung .gjs weist dabei auf den vom Framework generierten JavaScript-Code hin. Nun lassen sich alle Methoden der Presenter-Schnittstelle auf dem Client in denkbar einfacher Weise verwenden, z.B. searcher.search(

psenv::pushli();$env->object_param[0]=“’searchString'“; eval($_oclass[„“]); psenv::popli(); ?>

.value);, um die Suche auf dem Server mit dem Wert aus dem Input-Feld mit ID searchString zu starten. (Der

psenv::pushli(); eval($_oclass[„“]); psenv::popli(); ?>

-Operator von Prototype liefert das Dom-Objekt bei Übergabe einer ID oder des Dom-Objekts selbst.) Außerdem müssen noch die Callback-Methoden der View-Schnittstelle implementiert werden:

searcher.view.displayList = function(bookList) {
  jayjax.Dom.clear('books');
  bookList.each(function(book){
    var tr = "" + book.title + "" + book.author + "";
    new Insertion.Bottom('books', tr);
  });
}

Im Beispiel wird unter Zuhilfenahme einer JayJax- und zweier Prototype-Bibliotheksfunktionen zunächst die HTML-Anzeigetabelle für die Bücherliste gelöscht und anschließend mit allen anzuzeigenden Büchern zeilenweise wieder aufgefüllt. Auf die Properties der Book-Instanzen kann dabei über einfache Punkt-Notation zugegriffen werden, während auf Java-Seite die Bean-Konvention (get/set/is) zur Anwendung kommt. Fertig! Alle weiteren teuflischen Details des Beispiels lassen sich dem war-File mit Beispielanwendungen entnehmen, das der JayJax-Distribution beiliegt.

Testen

Damit die Rede vom testgetriebenen Entwickeln nicht nur eine Floskel bleibt, in Listing 2 noch ein Beispiel im JUnit 3.8-Stil, welche das Testen des Presenters BookSearcher mithilfe eines Dummy Views verdeutlicht. Statt der inneren Dummy-Klasse könnte natürlich ebenso gut ein dynamisches Mock-Objekt à la EasyMock zum Einsatz kommen.

Listing 2
-------------------------------------------------------------------------------------------------------
public class BookSearcherTest extends TestCase {
  private List books = null;
  private BookSearcher searcher;
  class DummyView implements BookSearchView {
    public void displayList(List resultList) {
      books = resultList;
    }
  }
  protected void setUp() throws Exception {
    searcher = new BookSearcher();
    searcher.setView(new DummyView());
  }
  public void testInit() throws Exception {
    searcher.init();
    assertTrue(books.isEmpty());
  }
  public void testSimpleSearch() throws Exception {
    searcher.search("topic");
    //The current implementation always displays 3 books
    assertEquals(3, books.size());
  }
  public void testEmpytSearch() throws Exception {
    searcher.search("");
    assertTrue(books.isEmpty());
    searcher.search("  ");
    assertTrue(books.isEmpty());
    searcher.search(null);
    assertTrue(books.isEmpty());
  }
}

Auf JavaScript-Seite sind automatisierte Unit-Tests noch nicht weit verbreitet, dennoch gibt es auch hier mittlerweile einige vielversprechende Ansätze, die sich beispielsweise bei Ajaxian unter dem Stichwort Testing finden lassen. Bei der Entwicklung von JayJax kommt zurzeit das in script.aculo.us enthaltene Test-Framework zum Einsatz.

Fazit

Art und Granularität der Kommunikation sind bei AJAX-basierten Applikationen wichtige Grundsatzfragen, da von den Entscheidungen in diesem Bereich nicht nur das Programmiermodell maßgeblich beeinflusst wird, sondern auch Volumen und Häufigkeit des Netzwerkzugriffs. Der Model-View-Presenter-Ansatz bietet sich für so manche Single Page Application an, denn er erlaubt nicht nur die vollständige Trennung von Logik und Präsentationsaspekten, sondern unterstützt gleichzeitig das in AJAX bevorzugte asynchrone Kommunikationsmuster.

Johannes Link ist Softwareentwickler, -coach und -aktivist, Bücherleser und Buchschreiber. Sein Webtagebuch findet sich auf jlink.blogger.de.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.