Technologia JSP umożliwia tworzenie własnych bibliotek znaczników, wykonujących jakieś akcje. Warto zauważyć, że programista może przejąć całkowitą kontrolę nad wykonaniem akcji, to znaczy:
wyspecyfikować jej parametry
oprogramować metody wykonywane przy znaczniku otwierającym, po przejściu przez ciało akcji oraz przy znaczniku zamykającym
tworzyć akcje zagnieżdżone
Aby dołączyć do strony JSP obsługę bibioteki znaczników, należy użyć dyrektywy taglib
.
Przykład:
<%@ taglib prefix="moje" uri="taglib.tld" %> <moje:terminarz miesiac="Grudzien" > <moje:termin dzien="2"> Spotkanie z premierem. </moje:termin> <moje:termin dzien="1"> Wycieczka do Brukseli. </moje:termin> </moje:terminarz>
Wynik działania takich akcji może być na przykład taki:
1, Niedziela
2, Poniedziałek
Każdemu zdefiniowanemu znacznikowi (czyli akcji) odpowiada klasa w Javie, która opisuje jego zachowanie. O tym, która klasa jest związana z daną akcją, mówi deskryptor biblioteki znaczników, którego położenie podaje się w dyrektywie taglib
. Deskryptor to plik w formacie XML, podający także atrybuty znacznika i inne jego cechy. Deskryptor dla powyższego przykładu to:
<?xml version = '1.0' encoding = 'UTF-8'?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> <taglib> <tlibversion>1</tlibversion> <jspversion>1.1</jspversion> <shortname>terminarze</shortname> <tag> <name>termin</name> <tagclass>terminarz.TerminTag</tagclass> <attribute> <name>dzien</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> <tag> <name>terminarz</name> <tagclass>terminarz.TerminarzTag</tagclass> </tag> </taglib>
Jak można zauważyć powyżej, przy opisie znacznika podajemy:
nazwę (name
) znacznika
klasę obsługującą znacznik (tagclass
)
jego parametry (attribute
), przy czym dla każdego z nich podajemy:
nazwę (name
)
czy jest wymagany (required
- opcjonalnie)
czy wartość parametru ma być obliczona dopiero podczas wykonywania się strony, gdy pochodzi ona np. z wyrażenia (rtexprvalue
- opcjonalnie)
Klasy te muszą implementować interfejs javax.servlet.jsp.tagext.Tag
. Na ogół jednak dziedziczą z klas TagSupport
lub BodyTagSupport
. Specyfikacja JSP 1.2 wyróżnia trzy typy znaczników:
proste (interfejs Tag) - znaczniki nie zmieniające ciała akcji, które jest wykonywane tylko jeden raz
iteracyjne (wprowadzone w specyfikacji JSP 1.2, interfejs IterationTag) - dzięki wprowadzeniu metody doAfterBody
możliwe jest wielokrotne wykonywanie ciała akcji, ale nie można go zmieniać
obsługujące ciało (interfejs BodyTag) - pozwala na zmienianie ciała akcji, które może być wykonywane wielokrotnie
Powyższy rysunek przedstawia przepływ sterowania podczas wykonywania się akcji związanej ze znacznikiem prostym. Kontener JSP najpierw odnajduje, przy użyciu deskryptora biblioteki znaczników, klasę odpowiedzialną za obsługę danego znacznika i tworzy obiekt tej klasy. Następnie wykonuje jej metody setPageContext()
oraz setParent()
. Pierwsza z nich ustawia atrybut pageContext
obiektu, zawierający kontekst strony, na której znajduje się znacznik. Druga ustawia atrybut parent
obiektu wskazujący obiekt, który odpowiada znacznikowi nadrzędnemu, którego ciało zawiera nasz znacznik.
Następnie kontener JSP wywołuje metodę doStartTag()
obiektu znacznika. Metoda ta może zwrócić dwie wartości EVAL_BODY_INCLUDE
lub SKIP_BODY
. Jeżeli zwrócona zostanie pierwsza wartość, wykonane zostanie ciało znacznika (akcji). W przeciwnym przypadku ciało znacznika zostanie pominięte.
W kolejnym kroku kontener wywołuje metodę doEndTag()
obiektu znacznika, która może zwrócić wartość SKIP_PAGE
lub EVAL_PAGE
. Wykonanie strony jest odpowiednio: wstrzymywane lub kontynuowane.
Przypuśćmy, że chcemy stworzyć prosty znacznik, wstawiający na stronę tekst pozdrowienia. Taka strona JSP miałaby postać:
<%@ taglib prefix="moje" uri="przywitania.tld" %> <moje:czesc jezyk="polski"/> użytkowniku.
Pokażę, jak napisać ten prosty znacznik na dwa sposoby.
TagSupport
import java.io.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; public class CzescTag extends TagSupport { String jezyk; public void setJezyk (String jezyk) { this.jezyk = jezyk; } public int doStartTag () throws JspException { try { JspWriter out = pageContext.getOut (); if (this.jezyk.compareTo ("polski")) out.print ("Czesc"); else out.print ("Hello"); } catch (IOException e) { throw new JspException ("Wystapil wyjatek IOException"); } return SKIP_BODY; } public int doEndTag () throws JspException { return EVAL_PAGE; } }
Tag
import java.io.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; public class CzescTag implements Tag { private PageContext pageContext; private Tag parent; String jezyk; public CzescTag () { super (); } public setJezyk (String jezyk) { this.jezyk = jezyk; } public int doStartTag () throws JspException { try { JspWriter out = pageContext.getOut (); if (this.jezyk.compareTo ("polski")) out.print ("Czesc"); else out.print ("Hello"); } catch (IOException e) { throw new JspException ("Wystapil wyjatek IOException"); } } public int doEndTag () throws JspException { return EVAL_PAGE; } public void release () { } public void setPageContext (PageContext pageContext) { this.pageContext = pageContext; } public void setParent (Tag parent) { this.parent = parent; } public Tag getParent () { return this.parent; } }
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-// Sun Microsystems, Inc.// DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/ web-jsptaglibrary_1_1.dtd"> <taglib> <tlibversion>1.0</tlibversion> <jspversion>1.1</jspversion< <shortname>first</shortname> <tag> <name>hello</name> <tagclass>tags.HelloTag</tagclass> <bodycontent>empty</bodycontent> <attribute> <name>jezyk</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> </taglib>
<taglib> <taglib-uri>przywitania.tld</taglib-uri> <taglib-location>/WEB-INF/jsp/przywitania.tld</taglib-location> </taglib>
Znacznik ten pozwala na wielokrotne przetwarzanie ciała akcji, które dodatkowo można modyfikować. Przepływ sterowania wygląda tak samo, jak w przypadku prostego znacznika, z tym wyjątkiem, że po wykonaniu ciała akcji wywoływana jest metoda doAfterBody()
, która pozwala na manipulację tymże ciałem. Dzięki niej możemy także zmusić kontener do kolejnego przetworzenia ciała akcji.
Klasa obsługująca znacznik:
import java.io.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; public class MaleLiteryTag extends BodyTagSupport { public int doAfterBody() throws JspException { try { BodyContent bc = getBodyContent(); String body = bc.getString(); JspWriter out = bc.getEnclosingWriter(); if (body != null) { out.print(body.toLowerCase()); } } catch(IOException e) { throw new JspException("Wystapil wyjatek IOException"); } return SKIP_BODY; } }
Strona JSP korzystająca ze znacznika:
<%@ taglib uri="taglib.tld" prefix="moje" %> <html> <head> <title>Manipulacja</title> </head> <body> <moje:malelitery> Witamy w swiecie technologii JSP. </moje:malelitery> </body> </html>
Wpis w deskryptorze biblioteki znaczników:
<tag> <name>malelitery</name> <tagclass>MaleLiteryTag</tagclass> <bodycontent>JSP</bodycontent> </tag>Wynik działania
Klasa obsługująca znacznik:
import java.io.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; public class IterowanieTag extends BodyTagSupport { int ile = 0; BodyContent bodyContent; public void setIle(int ile) { this.ile = ile; } public int doStartTag() throws JspException { if (ile > 0) { return EVAL_BODY_TAG; } else { return SKIP_BODY; } } public void setBodyContent(BodyContent bodyContent) { this.bodyContent = bodyContent; } public int doAfterBody() throws JspException { if (ile >1) { ile--; return EVAL_BODY_TAG; } else { return SKIP_BODY; } } public int doEndTag() throws JspException { try { if (bodyContent != null) { bodyContent.writeOut(bodyContent.getEnclosingWriter()); } } catch (IOException e) { throw new JspException("Wystapil wyjatek IOException"); } return EVAL_PAGE; } }
Strona JSP korzystająca ze znacznika:
<%@ taglib uri="taglib.tld" prefix="moje" %> <html> <head> <title>Iterowanie</title> </head> <body> <moje:iterowanie ile="3"> Witamy w swiecie technologii JSP.<br> </moje:iterowanie> </body> </html>
Wpis w deskryptorze biblioteki znaczników:
<tag> <name>iterowanie</name> <tagclass>IterowanieTag</tagclass> <bodycontent>JSP</bodycontent> <attribute> <name>ile</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>Wynik działania
Klasa obsługująca znacznik:
import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.io.Writer; import java.io.IOException; import java.util.ArrayList; public class TerminarzTag extends BodyTagSupport { String terminy[]; public void dodajTermin (int dzien, String opis) { if (opis != null) terminy[dzien-1] = opis; } public int doStartTag () throws JspException { terminy = new String[31]; return EVAL_BODY_TAG; } public int doEndTag () throws JspException { for (int i = 0; i < 31; i++) { String opis = terminy[i]; if (opis != null) { JspWriter out = pageContext.getOut(); try { out.println("<h2>" + (i+1) + "</h2>"); out.println(opis); out.newLine (); } catch (IOException e) { throw new JspException (); } } } return super.doEndTag (); } } import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.io.Writer; import java.io.IOException; import java.util.ArrayList; public class TerminTag extends BodyTagSupport { int dzien; public void setDzien (int d) { this.dzien = d; } public int doStartTag () throws JspException { return EVAL_BODY_TAG; } public int doEndTag () throws JspException { TerminarzTag trm = (TerminarzTag)getParent (); BodyContent body = getBodyContent (); String s = body.getString (); if (dzien != 0 && s != null) trm.dodajTermin (dzien, s); return super.doEndTag (); } }
Strona JSP korzystająca ze znacznika:
<%@ page contentType="text/html; charset=Windows-1250" %> <%@ taglib prefix="moje" uri="/WEB-INF/terminlib.tld" %> Terminarz na nadchodzący miesiąc <moje:terminarz> <moje:termin dzien="2"> Spotkanie z ministrem łšcznoci. </moje:termin> <moje:termin dzien="1"> Spotkanie z prezydentem. </moje:termin> </moje:terminarz>
Wpis w deskryptorze biblioteki znaczników:
<tag> <name>terminarz</name> <tagclass>TerminarzTag</tagclass> <bodycontent>JSP</bodycontent> <info>terminarz</info> </tag> <tag> <name>termin</name> <tagclass>TerminTag</tagclass> <bodycontent>JSP</bodycontent> <info>termin</info> <attribute> <name>dzien</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>Wynik działania