programmera.net -> jsp -> normal     för utskrift      info@programmera.net

Märkesbibliotek

1. Vad är ett märkesbibliotek?
2. Ett första bibliotek
3. Märkesbiblioteket
4. Märkeshanterarna
5. Använda ett märkesbibliotek i JSP
6. Alternativt sätt att använda märkesbibliotek i JSP

1. Vad är ett märkesbibliotek?

Märkesbibliotek kapslar in funktionalitet som kan anropas från en JSP-sida. Märkesbibliotek anropar alla metoder via XML-märken. Att anropa metoder via XML-märken anses göra koden mer lättläst. XML är också lättare för t. ex. en gränssnittsdesigner att använda, eftersom denne känner igen syntaxen från HTML.

2. Ett första bibliotek

För att skapa och använda ett märkesbibliotek måste man göra mist 3 saker:

  1. Skapa ett bibliotek, t.ex. WEB-INF/ollesTaglib.tld.
  2. Skapa en klass som som utför själva arbetet, en så kallad märkeshanterare och kompilera den, t.ex. WEB-INF/nu/programmera/ollesTaglib/Now.java. En märkeshanterare kan bara hantera ett märke. Ett märkesbibliotek innehåller vanligtvis många märken, så många märkeshanterare måste därför skapas.
  3. Skapar en JSP-sida som använder märket, t.ex. /jsp/testtaglib.jsp.

3. Märkesbiblioteket

Filen innehållande märkesbiblioteket slutar på .tld och placeras någonstans under WEB-INF. Filen fungerar som en tabell över de märken som biblioteket innehåller. Nedan skapar vi ett märkesbibliotek som har två märken:

  • Det första märket now returnerar dagens datum. Märket tar inga argument och har ingen kropp.
  • Det andra märket square tar argumentet number och returnerar kvadraten på talet. Märket har ingen kropp.
Nedan listas ollesTaglib.tld:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
    PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>ollesTaglib</short-name>
  <description>A test.</description>
  <tag>
    <name>now</name>
    <tag-class>nu.programmera.ollesTaglib.Now</tag-class>
    <body-content>empty</body-content>
  </tag>
  <tag>
    <name>square</name>
    <tag-class>nu.programmera.ollesTaglib.Square</tag-class>
    <body-content>empty</body-content>
    <attribute>
	    <name>number</name>
	    <required>true</required>
    </attribute>
  </tag>
</taglib>  
Vi ska köra biblioteket i webbapplikationen examples som automatiskt installeras av Tomcat. Filen får sökvägen CATALINA_HOME/webapps/examples/WEB-INF/ollesTaglib.tld. Nedan förklaras vad de olika elementen under tag innebär:

Element Nödvändigt Exempel Beskrivning
name  Ja  square  Märkets namn är det man använder för att anropa märket i en JSP-sida.
tag-class  Ja  nu.programmera.ollesTaglib.Square  Sökvägen från /WEB-INF/classes/ till den klass som implementerar märket. Hela paketsökvägen måste beskrivas.
body-content  Nej  empty  Detta element bestämmer om märket kan ha en kropp eller inte. empty=ingen kropp. JSP=kroppen tolkas (körs) innan den lämnas över till märkeshanteraren. tagdependent=kroppen lämnas över till märkeshanteraren utan att köras.
attribute  Nej  -  Om man ska kunna skicka attribut till märket ska de deklareras här. Attributen har egna element som beskrivs nedan..

Elementet attribute har egna underelement. De beskrivs i tabellen nedan:

Element Nödvändigt Exempel Beskrivning
name  Ja  number  Attributets namn är det man använder för att sätta attributet i JSP-sidan.
required  Nej  true  Om attributet måste sättas då man använder märket.
rtexprvalue  Nej  true  Om attributet kan ta ett script-uttryck som värde.

Fler exempel på märkesbibliotek hittar du på CATALINA_HOME/webapps/example/WEB-INF/jsp/example-taglib.tld som levereras med tomcat. Detta märkesbibliotek innehåller 3 märken (tag): ShowSource, foo och log.

4. Märkeshanterarna

För varje märke i ett märkesbibliotek finns det en märkeshanterare som utför själva arbetet. I vårt märkesbibliotek ollesTaglib.tld definierades två märken; now och square. Märket now har elementet tag-class satt till nu.programmera.ollesTaglib.Now. Detta betyder att om märket now ska kunna användas måste det finnas en klass Now.class med sökvägen CATALINA_HOME/webapps/examples/WEB-INF/classes/nu/programmera/ollesTaglib/Now.class.

  • Anledningen till den relativt långa sökvägen är att det är standard att alla klasser ska ligga i ett paket som börjar med ditt företags domännamn baklänges. Domänen programmera.nu (programmera.nets gamla namn) blir alltså katalogerna nu/programmera/.
Vi vill att märket now ska skriva ut dagens datum. Now.java kodas på detta sätt:
package nu.programmera.ollesTaglib;

import java.util.Date;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class Now extends TagSupport{
  public int doStartTag() throws JspException{
    try{
      JspWriter out=pageContext.getOut();
      Date d= new Date(); 
      out.print(d);
    }catch(Exception e){
      System.err.println("Exception in Date");
      System.err.println(e.toString());
      throw new JspException(e.toString());
    }
    return SKIP_BODY;
  }

  public int doEndTag() throws JspException{
    return EVAL_PAGE;
  }
}
Märket square har som uppgift att räkna ut kvadraten av ett tal. Square.java har följande innehåll:
package nu.programmera.ollesTaglib;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class Square extends TagSupport{
  private int number;
  public void setNumber(int i){
    number=i;
  }  
  public int doStartTag() throws JspException{
    try{
      JspWriter out=pageContext.getOut();
      int i= (int) Math.pow(number,2);
      out.print(i);
    }catch(Exception e){
      System.err.println("Exception in SquareTag");
      System.err.println(e.toString());
      throw new JspException(e.toString());
    }
    return SKIP_BODY;
  }

  public int doEndTag() throws JspException{
    return EVAL_PAGE;
  }
}
Nu är det dags för lite teori:
  • En märkeshanterare måste antingen implementera gränssnitten Tag eller BodyTag ELLER ärva av TagSupport eller BodyTagSupport. Dessa klasser och gränssnitt finns i paketet javax.servlet.jsp.tagext. I vårt exempel valde vi att ärva från TagSupport.
Tabellen nedan förklaraa klasserna och gränssnitten mer i detalj:

Term Typ Beskrivning
Tag  Gränssnitt  Grundläggande gränssnittet för en märkeshanterare. Definierar konstanterna: EVAL_PAGE, SKIP_BODY, SKIP_PAGE och metoderna: doStartTag(), doEndTag(), getParent(), release().
IterationTag  Gränssnitt  Detta gränssnitt ärver från Tag och lägger till en ytterligare kostant: EVAL_BODY_AGAIN, och en metod: doAfterBody().
BodyTag  Gränssnitt  Ärver från IterationTag och lägger till följande metod: doInitBody(). Med denna metod kan man manipulera märkets kropp.
TagSupport  Klass  Denna klass implementerar Tag. Klassen används som bas för märkeshanterare. Ärv från denna klass då du ska göra ett märke som inte kommer att ha någon kropp.
BodyTagSupport  Klass  Denna klass implementerar BodyTag. Ärv från denna klass om du ska programmera ett märke som har en kropp. Denna klass har utöver de definierade av BodyTag även variabeln bodyContent och metoderna: getBodyContent(), getPreviousOut().

Följande kodsnutt förklarar när de olika metoderna anropas:

<myLib:myTag arg1="hej"> // doStartTag() anropas
Här är märkets kropp.. 
bla bla bla          // doAfterBody() aropas
</myLib:myTag> // doEndTag() anropas
// release() anropas
Nedan ges en utförligare beskrivning:

Metod Returnerar Beskrivning
doStartTag()  EVAL_BODY_INCLUDE | SKIP_BODY  Denna metod anropas då startmärket har lästs av webb-behållaren. Alla eventuella argumet har satts och kan användas, men märkets kropp vet man inget om. Om märket inte har någon kropp är denna metod den enda man behöver överrida om man ärver från TagSupport. Om SKIP_BODY returneras struntar webb-behållaren i märkets kropp och hoppar direkt till doEndTag().
doAfterBody()  EVAL_BODY_AGAIN | SKIP_BODY  Denna metod definieras av IterationTag och används för att kolla om kroppen kommer att återevalueras. Om metoder returnerar EVAL_BODY_AGAIN återevalueras kroppen. Om SKIP_BODY returneras så anropas doEndTag().
doEndTag()  EVAL_PAGE | SKIP_PAGE  Denna metod anropas då slutmärket läses av webb-behållaren. Om SKIP_PAGE returneras ignorerar webb-behållaren resten av JSP-sidan.
PageContext.getOut()  JspWriter  När man ska skriva ut sitt resultat till webbsidan gör man detta anrop för att få något som motsvarar ett Out-objekt.
release()  void  Denna metod anropas för att märkeshanteraren ska släppa sitt tillstånd.

Övriga påpekanden:
  • Klassen Square.java har en privat variabel number. Denna variabel behöver (som för JavaBean) en publik setNumber-metod. Däremot behöver man inte skriva någon getNumber-metod. Som vi ser i nästa avsnitt anropas setNumber() automatiskt om man skriver number=21 då märket används. Alla set-metoder anropas innan doStartTag().

5. Använda ett märkesbibliotek i JSP

För att testa om märkesbiblioteket fungerar skapar vi en JSP-sida med följande innehåll:
<HTML>
<HEAD>
<%@ taglib uri="/WEB-INF/ollesTaglib.tld" prefix="ot" %>
</HEAD>
<BODY>
<H1>Test av Olles Taglib</H1>
NU: <ot:now /></BR>
21 upphöjt till två: <ot:square number="21" />
</BODY>
</HTML>
Vi sparar filen så den får följande sökväg: CATALINA_HOME/webapps/examples/jsp/testtaglib.jsp. Vi öppnar en browser och ser följande:



6. Alternativt sätt att använda märkesbibliotek i JSP

Om man tycker att det är fult att skriva en absolut sökväg till märkesbiblioteket direkt i JSP-sidan kan man lägga den absoluta sökvägen i web.xml:
<taglib>
  <taglib-uri>ollesTaglib</taglib-uri>
  <taglib-location>/WEB-INF/ollesTaglib.tld</taglib-location>
</taglib>
Vi ändrar i JSP-sidan till:
<HTML>
<HEAD>
<%@ taglib uri="ollesTaglib" prefix="ot" %>
</HEAD>
<BODY>
<H1>Nytt test av Olles Taglib</H1>
NU: <ot:now /></BR>
21 upphöjt till två: <ot:square number="21" />
</BODY>
</HTML>
Vi sparar med namnet testtaglib2.jsp. Innan du öppnar din browser och testar måste webb-behållaren (alltså Tomcat) startas om, ty det är endast vid uppstart som web.xml tolkas.