Wicket Markup Komponenten – braucht man das?

Hi,

ich habe heute aus reinem Interesse ein wenig mit Wicket Templates herumgespielt. Die Frage war, ist es möglich, einen eigenen Tag in einem Wicket-Template (pures Html) zu deklarieren und auf diese Weise dynamisch Komponenten zu erzeugen?

Ein möglicher Use-Case hierfür wäre ein Tag der folgenden Art:

[code language=”html”]

<md:cms contentId="myContentId"/>

[/code]

Dies würde es erlauben, Inhalte eines Content-Management-Systems auf sehr einfache Weise im Html-Markup zu deklarieren. Natürlich muss klar sein, dass man so einige wichtige Wicket-Konzepte praktisch aushebelt:

  • Trennung von Logik und Layout
  • Komponentenlogik in Java (beispielsweise überschreiben von isVisible ist nicht möglich)
  • Ein Wicket-Entwickler der von diesem Ansatz nichts weiß, braucht evtl. ein wenig, um die richtige Stelle zu finden
  • Nicht Wicket-Standard-konform

Prinzipiell ist meine Meinung, dass ich diesen Ansatz wohl eher nicht verfolgen würde, und zwar primär aus den letzten beiden Punkten, aber mich würde brennend interessieren, was ihr dazu meint, ansonsten wollen wir hier erstmal feststellen, ob das überhaupt funktioniert.

Zunächst muss die Frage geklärt werden, wie Wicket überhaupt den zugehörigen Markup parsed.
Die Klasse die hierfür zuständig ist, ist die Klasse org.apache.wicket.markup.MarkupParser.

Diese Klasse geht einfach gesagt durch jedes (X)Html-Template, das gerendert wird und erzeugt entsprechende Tags, mit denen Wicket arbeiten kann. Primär sind dies natürlich Instanzen von org.apache.wicket.markup.WicketTag für Komponenten, wie die folgende Deklaration:

[code language=”html”]

<wicket:message key="my.test.key"/>

[/code]

Zunächst mal ist klar, dass wir den MarkupParser ein wenig anpassen müssen, bzw. bietet uns Wicket die schöne Möglichkeit,
den MarkupParser durch eigene Filter (Instanzen von org.apache.wicket.markup.IMarkupFilter) zu erweitern. Das ganze könnte wie folgt
aussehen:

[code language=”java”]

getMarkupSettings().setMarkupParserFactory(new MarkupParserFactory(){
@Override
public MarkupParser newMarkupParser(MarkupResourceStream resource) {
MarkupParser parser = super.newMarkupParser(resource);
parser.appendMarkupFilter(new AbstractMarkupFilter() {

public MarkupElement nextTag() throws ParseException {
ComponentTag tag = (ComponentTag)getParent().nextTag();
if(tag != null && "md".equals(tag.getNamespace()) && "cms".equals(tag.getName())){
MyCmsTag cmsTag = new MyCmsTag(tag);
my.setId(tag.getAttribute("id"));
return my;
}</pre>
return tag;
}
});
return parser;
}
});

[/code]

Die Methode nextTag wird von Wicket für jeden einzelnen Tag im Html-Markup aufgerufen, also beispielsweise auch für Tags wie <html/>, <body/> aber auch für unseren <md:cms/> – Tag.

Diejenige Klasse, die beispielsweise die <wicket:message/>-Tags auswertet ist die Klasse org.apache.wicket.markup.parser.filter.WicketMessageTagHandler (deren Implementierung übrigens interessant und wahrscheinlich anders ist, als man das erwarten würde).

Was benötigen wir jetzt? Wir benötigen einen MarkupFilter, der unseren <md:cms/>-Tag auswertet.

Der Code hierfür könnte wie bereits weiter oben beschrieben, wie folgt aussehen:

[code language=”java”]
public MarkupElement nextTag() throws ParseException {
ComponentTag tag = (ComponentTag)getParent().nextTag();
if(tag != null && "md".equals(tag.getNamespace()) && "cms".equals(tag.getName())){
MyCmsTag cmsTag = new MyCmsTag(tag);
cmsTag.setId(tag.getAttribute("id"));
return cmsTag;
}
return tag;
}
[/code]

Der Filter geht einfach durch den gesamten Markup, und sollte er den entsprechenden Tag finden, erzeugt er eine Instanz von MyCmsTag.

[code language=”java”]
public class MyCmsTag extends ComponentTag {

private String text;
public MyCmsTag(ComponentTag tag) {
super(tag);
}
public String getContentId() {
return text;
}

public void setContentId(String text) {
this.text = text;
}

}

[/code]

Der Tag ist ein einfaches Pojo und dient lediglich dazu, die Daten aus dem Markup zu speichern, also beispielsweise
das definiert Attribut “contentId“, mit dem ein bestimmter Inhalt aus dem CMS abgeholt werden kann.

Das einzige, was jetzt noch fehlt, ist die Wicket-Komponente, die aus dem CmsTag eine echte Wicket-Komponente erzeugt, und zwar an der richtigen Stelle im Markup. Hierfür bietet Wicket das Konzept der IComponentResolver.

Interessante Beispiele hierfür sind beispielsweise WicketLinkResolver, WicketBorderResolver etc.

Wir schreiben für unsere Komponente einen eigenen Resolver:

[code language=”java”]

public class MyComponentResolver implements IComponentResolver {

public boolean resolve(MarkupContainer container, MarkupStream stream,
ComponentTag tag) {

if(tag instanceof MyCmsTag){
MyCmsTag cmsTag = (MyCmsTag)tag;
String text = cmsTag.getAttribute("contentId");
container.autoAdd(new CmsLabel("irgendeineId",text), stream);
}

return true;

}

}

[/code]

Initialisiert wird der Resolver mit folgender Code-Zeile in der init-Methode der Application:

[code]

getPageSettings().addComponentResolver(new MyComponentResolver());

[/code]

Besonders interessant ist folgende Methode:

[code]

container.autoAdd(new CmsLabel("irgendeineId",text), stream);

[/code]

Die Klasse CmsLabel ist eine fiktive Klasse, die eine Anbindung an ein CMS bereits bereitstellt. Wir sollten nicht vergessen, dass der Inhalt dieses Artikels nicht die Implementierung der Anbindung an ein CMS sein soll, sondern lediglich die Deklaration der Komponente im Markup.

Über die Verwendung der Methode autoAdd() können wir die Komponente in einen übergeordneten WebMarkupConttainer einordnen, ohne das geprüft wird, ob die entsprechende wicket:id vorhanden ist, und ohne das Wicket Exceptions wirft, wenn der Komponentenbaum in der Renderingphase verändert wird.

Starten wir jetzt eine Anwendung, und deklarieren wir unseren md:cms-Tag im Markup einer Page sehen wir, dass eine Komponente eingefügt wird (man könnte hier Testweise ein Label verwenden), und zwar ohne dass wir im Java-Code der entsprechenden Seite eine Java-Komponene instantiieren müssen.

Wie bereits am Anfang des Artikels erwähnt, ist dies meiner Ansicht nach nicht unbedingt der Weg, den man beschreiten sollte.

Trotzdem würde ich mich über Kommentare freuen.

Effective Trainings & Consulting - Martin Dilger



Hat Ihnen dieser Blog-Eintrag gefallen? Ich stelle in diesem Blog Informationen über Tools, Frameworks und Werkzeuge zur Verfügung, die mich produktiver machen. Vielleicht kann ich auch Ihnen helfen, produktiver zu werden.


Ich unterstütze Sie als freier Mitarbeiter bei der Entwicklung von Software-Projekten, Agiler Arbeit sowie Schulungen / Fortbildungen.


Jeden Tag ein bisschen produktiver - ab heute