Skip to content


Anfang und Ende

Ich wünsche allen ein gesundes neues Jahr. 2009 hat sich verabschiedet und 2010 zeigt sich bereits jetzt von seiner besten Seite (wenn man Schnee mag). Ich freue mich auf die vielen spannenden Themen, die das neue Jahr bereithält.

Auch wenn es anders kommt, als man sich denkt, wage ich hier doch mal ein paar Vorhersagen. Manches davon ist offensichtlich und wird vermutlich schon deshalb nicht eintreten, Anderes ist frei erfunden und hat damit die besten Chancen, zum Volltreffer zu werden.

Nachdem 2009 eigentlich ein Scala-Jahr war, wird es auch 2010 in diesem Bereich interessant bleiben. Zu meiner Überraschung konnte allerdings auch Java durch den Scala-Hype profitieren. Einige fühlten sich wohl durch die Möglichkeiten von Scala entsprechend angestachelt, dass selbst für Dinge wie Scala Traits Implementierungen (z.B. java-mixins) für Java geschrieben wurden. Java ist zwar etwas angestaubt aber eben noch nicht tot. Und was Google mit GWT und Android alles auf Basis von Java anstellt, zeigt deutlich, dass da noch Platz für Innovationen ist. Das bedeutet, dass es auch 2010 nicht verkehrt ist, auf Java zu setzen.

Nachdem JSF 2.0 wie zu erwarten war, nicht alle Probleme, die JSF bis jetzt angesammelt hat, lösen wird, gehe ich davon aus, dass wie schon 2009 immer mehr Projekte auch mit Wicket realisiert werden. Da Wicket als Basis schon sehr ausgereift ist, wird sich der Zustrom an Entwicklern vermutlich eher auf Integrationsthemen auswirken: Web-Worker, Javascript-Frameworks und andere noch unbekannte Entwicklungen. Ich erwarte außerdem (unabhängig von meinem eigenen Teilprojekt auf wicketstuff.org) ein paar Standardvorlagen für Wicket-Projekte.

Nachdem Groovy in der neuesten Version alle Sprachfeatures hat, die für eine saubere Wicket-Integration notwendig sind, wird es auch in diesem Bereich interessant. Die Bedenken, die ich trage, richten sich vielmehr an die Plattform Groovy/Grails an sich. Es gibt Entwicklungen, die bei mir Bauchschmerzen hervorrufen (Grape – Dependency Management als Annotation auf einem Import – WTF)), die merkwürdig sind und auf die Frage “Warum?” vermutlich nur mit einem “Weil es geht.” beantwortet werden.

Alles in Allem, es bleibt und wird spannend. Ich wünsche allen (unabhängig von der technologischen Präferenz) ein gutes und erfolgreiches Jahr.

Posted in Allgemein, JSF, Wicket.

Tagged with , , , , .


Wicket Ajax Tipp – AjaxRequestTarget nachträglich ermitteln

Gestern wurde ich gefragt, ob und wie man denn vermeiden kann, dass man das AjaxRequestTarget immer als Funktionsargument weiterreichen muss, wenn man an anderer Stelle darauf zugreifen möchte. Ich muss zugeben, dass ich die einfache Antwort nicht sofort wusste (was, wie man sehen wird, erstaunlich ist). Ich wusste aber, dass mir kürzlich ein Stück Wicket-Code begegnet ist, wo ermittelt werden musste, ob ein AjaxRequest oder ein normaler Request abgearbeitet wird.

Ich machte mich also erneut auf die Suche und fand die Code-Zeile in der AjaxFallbackButton-Klasse. Ich war kurz davor ein paar Zeilen Code daraus abzuleiten und warf nur einen flüchtigen Code in die AjaxRequestTarget-Klasse, um festzustellen, das es das natürlich schon gibt.

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. AjaxRequestTarget target=AjaxRequestTarget.get();

So einfach geht das. So einfach, dass ich es fast übersehen hätte.

Posted in Allgemein, Wicket.

Tagged with , , .


Wicket Form Submit – mit und ohne Ajax

Webanwendungen ohne Formulare gibt es nicht. Dabei ist die korrekte Formularbehandlung alles andere als trivial. Zum Glück bietet Wicket eine ausgezeichnete Unterstützung für Formulare, die kaum Wünsche offen lässt. Doch auch mit Wicket sind ein paar Dinge zu beachten, um die verschiedenen Interaktionsmöglichkeiten des Nutzers korrekt zu verarbeiten.

Wicket bietet die Möglichkeit, neben dem normalen Abschicken eines Formulars auch auf die angeklickten Submit-Buttons reagieren zu können. Damit man aber auch zuverlässig unterscheiden kann, ob der Nutzer das Formular durch ein Enter im Textfeld oder durch einen Klick auf einen Button abgeschickt hat, muss man ein paar Vorbereitungen treffen.

Für das Formular legen wir eine Bean an, welche die Daten aufnimmt.

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. package de.wicketpraxis.web.blog.pages.questions.form.submit;
  2. import java.io.Serializable;
  3. public class FormBean implements Serializable
  4. {
  5.   String _name;
  6.   
  7.   public String getName()
  8.   {
  9.     return _name;
  10.   }
  11.   
  12.   public void setName(String name)
  13.   {
  14.     _name = name;
  15.   }
  16. }

Für unser Beispiel benötigen wir ein Formular, einen AjaxFallbackButton, einen normalen Button und einen Button, den wir dazu benutzen, denn Fall herauszubekommen, wenn der Nutzer einfach nur Enter gedrückt hat. Das FeedbackPanel sollte man auch nicht vergessen.

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. package de.wicketpraxis.web.blog.pages.questions.form.submit;
  2. import org.apache.wicket.ajax.AjaxRequestTarget;
  3. import org.apache.wicket.ajax.markup.html.form.AjaxFallbackButton;
  4. import org.apache.wicket.markup.html.WebPage;
  5. import org.apache.wicket.markup.html.form.Button;
  6. import org.apache.wicket.markup.html.form.Form;
  7. import org.apache.wicket.markup.html.form.TextField;
  8. import org.apache.wicket.markup.html.panel.FeedbackPanel;
  9. import org.apache.wicket.model.CompoundPropertyModel;
  10. public class FormSubmitPage extends WebPage
  11. {
  12.   int _counter;
  13.   
  14.   public FormSubmitPage()
  15.   {
  16.     final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
  17.     feedbackPanel.setOutputMarkupId(true);
  18.     add(feedbackPanel);
  19.     
  20.     CompoundPropertyModel<FormBean> formModel = new CompoundPropertyModel<FormBean>(new FormBean());
  21.     Form<FormBean> form = new Form<FormBean>("form",formModel)
  22.     {
  23.       @Override
  24.       protected void onSubmit()
  25.       {
  26.         info("Form Submit: "+_counter++);
  27.       }
  28.     };
  29.     
  30.     form.add(new TextField<String>("name"));
  31.     
  32.     form.add(new Button("defaultSubmit")
  33.     {
  34.       @Override
  35.       public void onSubmit()
  36.       {
  37.         info("Default Button: "+_counter++);
  38.       }
  39.     });
  40.     
  41.     form.add(new Button("submit")
  42.     {
  43.       @Override
  44.       public void onSubmit()
  45.       {
  46.         info("Button: "+_counter++);
  47.       }
  48.     });
  49.     
  50.     form.add(new AjaxFallbackButton("ajaxSubmit",form)
  51.     {
  52.       @Override
  53.       protected void onSubmit(AjaxRequestTarget target, Form<?> form)
  54.       {
  55.         if (target!=null)
  56.         {
  57.           info("AjaxSubmit Button(ajax): "+_counter++);
  58.           target.addComponent(feedbackPanel);
  59.         
  60.           _counter=0;
  61.         }
  62.         else
  63.         {
  64.           info("AjaxFallbackButton: "+_counter++);
  65.         }
  66.       }
  67.     });
  68.     
  69.     add(form);
  70.   }
  71.   
  72.   @Override
  73.   protected void onBeforeRender()
  74.   {
  75.     _counter=0;
  76.     super.onBeforeRender();
  77.   }
  78. }

Damit man die Reihenfolge besser erkennen kann, mit der die onSubmit-Methoden aufgerufen werden, wird ein Zähler (_counter) hochgezählt und vor dem Darstellen des Formulars wieder zurückgesetzt. In diesem Beispiel liegt die entscheidende Information im Markup. Dabei ist die Reihenfolge der verschiedenen Submit-Komponenten wichtig.

 HTML |  copy code |? 
  1. <html>
  2.   <head>
  3.     <title>FormSubmitPage</title>
  4.   </head>
  5.   <body>
  6.     <div wicket:id="feedback"></div>
  7.     
  8.     <form wicket:id="form">
  9.       <input wicket:id="defaultSubmit" type="submit" value="" style="border:0px; width: 0px; height: 0px; visibility: collapse; display: compact;">
  10.       Name <input wicket:id="name"><br>
  11.       <input wicket:id="ajaxSubmit"  type="submit" value="AjaxButton">
  12.       <input wicket:id="submit" type="submit" value="Button">
  13.     </form>
  14.   </body>
  15. </html>

Der Button mit der Wicket-ID defaultSubmit muss vor allen anderen Submit-Komponenten stehen. Dadurch wird beim Abschicken des Formulars durch Enter dieser Button ausgewählt. Wenn einer der anderen Buttons angeklickt wird, dann wird das Formular über diesen Button abgeschickt. Doch warum soviel Aufwand? Die onSubmit()-Methode des Formulars wird doch in jedem Fall aufgerufen.

Der Aufwand ist notwendig, wenn man abhängig davon, ob der Nutzer auf einen Button oder eben auf keinen Button geklickt hat, eine Aktion ausführen möchte. Das bedeutet, dass man die Aktion nicht in onSubmit ausführen kann. Es ist allerdings auch nicht möglich, im onSubmit() der Buttonkomponenten ein Flag zu setzen, dass dann in der onSubmit()-Methode des Formulars aufgerufen wird, da die Methode beim AjaxFallbackButton erst nach dem onSubmit() der Komponente aufgerufen wird.

Folgende Ergebnisse erhält man, wenn man das Formular a) per Enter, b) per AjaxButton und c) per Button abschickt:

Submit per Enter

Submit per Enter

Submit per AjaxButton

Submit per AjaxButton

Submit per Button

Submit per Button

Wenn Javascript deaktiviert ist, dann erhält man folgende Ausgabe:

Submit per Enter

Submit per Enter

Submit per AjaxButton

Submit per AjaxButton

Submit per Button

Submit per Button

Wenn man dieses Vorgehen beherzigt, funktioniert die Anwendung auch mit deaktiviertem JavaScript genauso zuverlässig. Das freut den Nutzer und in diesem Fall auch den Entwickler:)

Posted in Technologie, Wicket.

Tagged with , , , , .


Praxisbuch Wicket – Übersicht

Hinterher ist man meistens schlauer, hofft man. Nachdem ich gerade per Twitter auf eine JSF-Mindmap gestoßen bin (dank @ptrthomas), dachte ich mir, dass es hilfreich sein könnte, die Mindmap, die ich mir bei der Arbeit am Buch erstellt habe, auch anderen zur Verfügung zu stellen. Perfektionismus führt manchmal dazu, dass man nie ankommt, daher hier sofort das ungefilterte Ergebnis.

Wicket Struktur - Übersicht aus der Arbeit am Buch

Wicket Struktur – Übersicht aus der Arbeit am Buch

Posted in Allgemein, JSF, Wicket.

Tagged with , , , .


E-Mail mit Wicket verschicken

Wäre es nicht praktisch, wenn man eine E-Mail verschicken könnte, in dem man die Komponenten, die man für die Darstellung auf der Webseite verwendet, auch für das Erstellen der E-Mail heranziehen könnte. Das Unterfangen ist leider nicht trivial, weil man nicht ohne Weiteres um die “Magie”, die Wicket an vielen Stellen benutzt, herum kommt. Nun bietet Wicket die Möglichkeit, Komponenten und Seiten, Formulare und vieles mehr in Unit-Test zu testen. Was liegt also näher, als diese Funktionalität für unsere Zwecke zu missbrauchen.

Die E-Mail

Als erstes erstellen wir eine Seite, die den Inhalt der E-Mail erzeugen soll:

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. package de.wicketpraxis.web.blog.pages.questions.email;
  2. import java.util.List;
  3. import org.apache.wicket.markup.html.WebPage;
  4. import org.apache.wicket.markup.html.basic.Label;
  5. import org.apache.wicket.markup.html.list.ListItem;
  6. import org.apache.wicket.markup.html.list.ListView;
  7. import org.apache.wicket.model.IModel;
  8. public class EmailContentPage extends WebPage
  9. {
  10.   public EmailContentPage(IModel<List<? extends String>> list)
  11.   {
  12.     add(new ListView<String>("list",list)
  13.     {
  14.       @Override
  15.       protected void populateItem(ListItem<String> item)
  16.       {
  17.         item.add(new Label("name",item.getModel()));
  18.       }
  19.     });
  20.   }
  21. }

Der Code soll zeigen, dass Komponenten ganz normal Funktionieren. Deshalb bekommt die Seite von außen eine Liste mit Werten übergeben, die dann durch eine ListView dargestellt wird.

 HTML |  copy code |? 
  1. <h1>TestEmail</h1>
  2. <ul>
  3.   <li wicket:id="list"><span wicket:id="name"></span></li>
  4. </ul>

Im Markup habe ich absichtlich den “Rahmen” der Seite (html > body) weggelassen. Die Klasse und das Markup dienen in diesem Beispiel nur als Platzhalter.

Darstellen und Abschicken

In Wicket sind sehr viele Objekte an den aktuellen Thread gebunden. Das ist notwendig, damit man nicht eine ganze Liste von Objekten als Übergabeparameter durch Methodenaufrufe schleifen muss. Das ist allerdings genau dann ein Problem, wenn wir innerhalb einer Wicketanwendung eine andere Seite darstellen möchten. Außerdem ist es ein Problem, wenn es wie in einem im Hintergrund laufenden Prozess überhaupt keine WicketApplication gibt. Daher erstellen wir uns ein paar Hilfsklassen, die sich um dieses Problem kümmern.

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. package de.wicketpraxis.web.blog.pages.questions.email;
  2. public interface WicketCallback<I,O>
  3. {
  4.   public O getResult(I input);
  5. }

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. package de.wicketpraxis.web.blog.pages.questions.email;
  2. import java.util.logging.Logger;
  3. public class WicketThreadAdapter<I,O> extends Thread
  4. {
  5.   private static final Logger _logger = Logger.getLogger(WicketThreadAdapter.class.getName());
  6.   
  7.   Object _lock=new Object();
  8.   
  9.   WicketCallback<I, O> _callback;
  10.   I _input;
  11.   O _output;
  12.   boolean _done=false;
  13.   
  14.   protected WicketThreadAdapter(WicketCallback<I, O> callback, I input)
  15.   {
  16.     _callback=callback;
  17.     _input=input;
  18.   }
  19.   
  20.   @Override
  21.   public void run()
  22.   {
  23.     synchronized (_lock)
  24.     {
  25.       _output=_callback.getResult(_input);
  26.       _done=true;
  27.       _lock.notify();
  28.     }
  29.   }
  30.   
  31.   protected O getResult() throws InterruptedException
  32.   {
  33.     synchronized (_lock)
  34.     {
  35.       while (!_done)
  36.       {
  37.         _lock.wait();
  38.       }
  39.       return _output;
  40.     }
  41.   }
  42.   
  43.   public static <I,O> O getResult(WicketCallback<I, O> callback, I input) throws InterruptedException
  44.   {
  45.     WicketThreadAdapter<I,O> threadAdapter = new WicketThreadAdapter<I,O>(callback,input);
  46.     threadAdapter.start();
  47.     return threadAdapter.getResult();
  48.   }
  49. }

Die WicketThreadAdapter-Klasse wird über die statische Methode getResult(…) angesprochen und macht folgendes: Es wird ein neuer Thread erzeugt. Im laufenden Thread wird der Callback aufgerufen und der Thread beendet. Das Ergebnis, das im Thread ermittelt wurde, wird als Ergebnis herausgereicht. Kurz: Das Ergebnis wird in einem anderen Thread als dem aktuellen ermittelt und führt so nicht zu Kollisionen mit der laufenden Anwendung.

Der Trick

Das alles führt noch nicht zum Ziel. Wir müssen uns jetzt noch darum kümmern, dass wir die Seite dargestellt bekommen. Dazu benutzten wir die Klasse BaseWicketTester, die sich um alles weitere kümmert. Dort übergeben wir die Seite, die wir darstellen wollen. Dann können wir auf den Inhalt der Seite zugreifen und als Ergebnis zurück liefern.

 Java(TM) 2 Platform Standard Edition 5.0 |  copy code |? 
  1. package de.wicketpraxis.web.blog.pages.questions.email;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. import org.apache.wicket.markup.html.WebPage;
  5. import org.apache.wicket.markup.html.basic.Label;
  6. import org.apache.wicket.model.IModel;
  7. import org.apache.wicket.model.Model;
  8. import org.apache.wicket.util.tester.BaseWicketTester;
  9. public class EmailFromComponentPage extends WebPage
  10. {
  11.   public EmailFromComponentPage()
  12.   {
  13.     WicketCallback<List<String>, String> callback = new WicketCallback<List<String>, String>()
  14.     {
  15.       public String getResult(List<String> input)
  16.       {
  17.         final IModel<List<? extends String>> listModel = Model.ofList(input);
  18.         
  19.         BaseWicketTester tester=new BaseWicketTester();
  20.         tester.startPage(new EmailContentPage(listModel));
  21.         return tester.getServletResponse().getDocument();
  22.       }
  23.     };
  24.     
  25.     String result;
  26.     try
  27.     {
  28.       result = WicketThreadAdapter.getResult(callback, Arrays.asList("Klaus","Susi","Bert"));
  29.     }
  30.     catch (InterruptedException e)
  31.     {
  32.       e.printStackTrace();
  33.       result=e.getLocalizedMessage();
  34.     }
  35.     
  36.     add(new Label("email",result).setEscapeModelStrings(false));
  37.   }
  38. }

Damit das Ergebnis richtig angezeigt wird, teilen wir dem Label “email” mit, dass es den Text nicht umschreiben soll.

 HTML |  copy code |? 
  1. <html>
  2.   <head>
  3.     <title>Email Form Component</title>
  4.   </head>
  5.   <body>
  6.     Email <span wicket:id="email"></span>
  7.   </body>
  8. </html>

Wenn man die Seite aufruft, erhält man dann folgende Darstellung:

Ergebnis: Html-E-Mail aus Wicket

Ergebnis: Html-E-Mail aus Wicket

Wenn man die E-Mail durch Hintergrundprozesse versenden möchte, ist ein eigener Thread unnötig, weil keine WicketApplication-Klasse an den Thread gebunden ist. Dann sollte der Einsatz der BaseWicketTester-Klasse reichen. Es ist allerdings darauf zu achten, dass alles, was in der eigenen Application-Klasse definiert wurde, an dieser Stelle nicht ohne weiteres zur Verfügung steht (z.B. wird der OpenSessionInViewFilter für Spring nicht aufgerufen, die SpringBean-Annotationen sind ebenfalls funktionslos).

Fazit

Auch wenn sicher noch die eine oder andere Anpassung notwendig ist, damit man Komponenten sowohl für die Anwendung als auch für den E-Mail-Versand benutzten kann, lohnt sich der Aufwand, weil man a) auf die vielfältigen Möglichkeiten, die Wicket als “Template-Engine” bietet, zurückgreifen kann, b) man sich im besten Fall doppelten Code ersparen kann und c) man keine zweite Lösung einbinden muss. Und wieder hat mich Wicket ein wenig überrascht. Weil es dann doch so einfach ging:)

Posted in Technologie, Wicket.

Tagged with , , , , .