$KK(Aufbau von Simple Classic Tag Libraries) $R(Objective) Describe the semantics of the "Simple" custom tag event model when the event method (doTag) is executed; write a tag handler class; and explain the constraints on the JSP content within the tag. $R\ $KKK(Vergleich mit den klassischen Tag Interfaces) Das SimpleTag Interface ist seit JSP 2.0 eingeführt, und vereinfacht die Programmierung von eigengeschreibenen Tags. Es hat folgende Vorteile: * einfacheres Interface * es ermöglicht beliebig komplexe Tags zu entwickeln * einheitliches Interface, egal für welche Art von Tags Es hat auch ein paar Nachteile: * Erst gültig ab JSP 2.0 also mit J2EE 1.4 * es können keine Skriplets im Body sein * Es wird bei jedem Aufruf eine neue Instanz des TagHandlers erzeugt. Man kann denselben TagHandler nicht cachen. $KKK(Das SimpleTag Interface) Der Programmierer schreibt Klassen, die das SimpleTag Interface erfüllen. Meist entwickelt er eine Unterklasse der abstrakten Klasse SimpleTagSupport. Diese Unterklasse überschreibt dann meistens nur die doTag() Methode. $B(Bilder,tagSupport.gif,Klassenhierarchie Simple Tag Support) Beachte: Das Simple Tag Interface erbt nicht vom Tag Interface. Die Custom TagHandler und die Simple TagHandler haben ausser Object keine gemeinsamen Klassen in der Vererbungshierarchie. Während bei den Classic Custom Tags es verschiedene Arten von Events gibt (doStartTag(), doEndTag() und doAfterBody() wird bei dieser Version nur der doTag() bei der Bearbeitung des Bodys ausgelöst. $B(Bilder,eventsSimpleTag.gif,Events bei Bearbeitung mit SimpleTag) I.a. wird der Programmierer eine Unterklasse von SimpleTagSupport schreiben. In dieser Unterklasse schreibt er für jedes übergebene Attribut eine entsprechende setter-Methode und überschreibt die doTag() Methode. Die Klasse SimpleTagSupport bietet einige Methoden, die die Implementierung des Tags erleichtern. * public void doTag() throws JspExeption, SkipPageException, java.io.Exception Dies ist i.a. die einzigste Methode die der Programmierer überschreibt, und wird beim Lesen des Bodys des Tags aufgerufen. Mit dem Werfen einer SkipPageException wird veranlasst dass der Rest der JSP Seite nicht weiter bearbeitet wird (entspricht TAG.SKIP_BODY Konstante, bei den Classic Custom Tag Interfaces). * public JspTag getParent() Dies gibt den TagHandler des übergeordneten Tags zurück. JspTag ist das Interface das alle TagHandler erfüllen, hat aber keine eigenen Methoden. D.h der Programmierer muss nach Anwendung der getParent() Methode das erhaltene Objekt auf die konkrete TagHandler Klasse casten. Hat der Tag keinen übergeordneten Tag, so wird null zurückgegeben * public void setParent(JspTag parent) Diese Methode wird meistens nur vom Container verwendet. * public static final JspTag findAncestorWithClass(JspTag from, Class klass) Findet den nächsten Vater des Tags from, der der Klasse klass entspricht. Intern wird die getParent() Methode verwendet. * public JspFragment getJspBody() Enthält den Body des Tags in Form eines JspFragment Tags. Die Verwendung wird weiter unten erklärt * public void setJspBody(JSPFragment body) Diese Methode wird meistens nur vom Container verwendet. * public JspContext getJspContext() Dies ist eine abstrakte Klasse. Die Klasse PageContext ist i.a. die einzigste konkrete Unterklasse von JspContext. JspContext dient zum Zugriff auf die Attribute der Scopes, und mit getOut() erhält man die aktuelle Ausgabe. Diese Methode wird sehr häufig angewendet. * public void set JspContext setJspContext(JspContext context) Diese Methode wird vom Container verwendet, und selten vom Programmierer direkt benutzt. $KKK(Beispiel für die Verwendung eines SimpleTag Interfaces) Es wird bewusst ein Beispiel mit einem Tag ohne Body genommen. Der Tag hat folgenden Aufbau $S() $S\ Es wird ausgegeben: $S() Hallo Hallo Klaus Hallo Herr Meucht Hallo Frau Meucht $S\ Der TagHandler wird folgendermassen programmiert $S() public class GrussTag extends SimpleTagSupport(){ private String name,sex ; // Für jedes Attribut benoetigt eine setter Methode public void setName(String name){ this.name = name; } public void setSex(String mOrf){ this.sex = mOrf; } // die doTag() Methode ist die einzigste Event-Methode mit SimpleTag Interfaces public void doTag() throws JspException, IOException{ PageContext pageContext = (PageContext) getJspContext(); HttpServletResponse resp = ( HttpServletResponse) pageContext.getResponse(); PrintWriter pw = resp.getWriter(); // Alternative JspWriter pw = getJspContext().getOut(); // JspWriter funktioniert wie PrintWriter, kann zusaetzlich Daten zwischenspeichern. if (name == null){ pw.println("Hallo"); return ; } if (sex == null){ pw.prinln("Hallo " + name ); return ; } if ( sex.compareToIngoreCase("m")== 0 ){ pw.println("Hallo Herr " + name ); }else{ pw.println("Hallo Frau " + name ); } } } $S\ $KKK(Zugriff auf den Body des Tags) Ein TagHandler der das SimpleTag Interface erfüllt, getJspBody() und sollte ein JspFragment Objekt zurückgeben. Das JspFragment kapselt den Body und hat nur 2 Methoden. * getJspContext() * invoke(Writer) Die Methode invoke(Writer) schreibt die Auswertung des Body in das Argument Writer. Ist der Wert des Arguments null so wo wird der Body, in den Writer, der sich durch getJspContext().getOUt() ergibt, ausgegeben. In diesem Fall wird der Body in die normale Ausgabe geleitet. Immer dann wenn den Body selbst nicht bearbeiten will, übergibt man den null Wert als Argument der invoke() Methode. Immer dann wenn man den Body bearbeiten will, kann man ein eigenes Writer Objekt erzeugen, und darin die Ausgabe puffern, und dann in die Ausgabe zurückgeben. $S() ... StringWriter evalResult = new StringWriter(); StringBuffer buffer = evalResult.getBuffer(); body.invoke(evalResult); // Die Auswertung des Body steht nun in buffer bearbeite(buffer); getJspContext().getOut().print(buffer); ... $S\ $KKK(Schleifen mit dem SimpleTag Interface) Schleifen werden intern in der doTag() Methode implementiert. Ein einfaches Beispiel verdeutlicht dies. $S() $S\ Der entsprechende TagHandler kann folgendermassen implementiert werden: $S() // imports werden weggelassen puclic class ForTokenTagHandler extends SimpleTagSupport{ private Collection items ; private String var ; public setItems(Collection items){ this.items = items ; } public setVar(String var){ this.var = var ; } public void doTag() throws JspException,IOException { JspFragment body = getJspBody(); if (body != null){ Iterator iter = items.getIterator(); while (iter.hasNext()){ Object currentValue = iter.next(); getJspContext().setAttribute(var, currentValue); body.invoke(null); } } } } $S\ Beachte: * Die Iteration wird in der doTag() Methode abgehandelt. * In der doTag() Methode wird am Beginn der Iteration, ein Attribut im PageScope gesetzt. Das Attribut hat als key den Wert der Variable var (im Beispiel "current"). Der Wert des Attribut ist das aktuelle Token in der Iteration. * Bei der Auswertung des Bodys, durch die Methode invoke(null), wird der Wert des Attributs "current" ausgegeben.