JSPテンプレート

Web開発ツールは急速に進歩していますが、SwingやVisualWorks Smalltalkなどのほとんどのグラフィカルユーザーインターフェイス(GUI)ツールキットに遅れをとっています。たとえば、従来のGUIツールキットは、レイアウトアルゴリズムをカプセル化して再利用できるようにする、何らかの形式のレイアウトマネージャーを提供します。この記事では、レイアウトマネージャーと同様にレイアウトをカプセル化して複製する代わりに再利用できるJavaServer Pages(JSP)のテンプレートメカニズムについて説明します。

レイアウトは開発の過程で多くの変更を受けるため、その機能をカプセル化して、アプリケーションの他の部分への影響を最小限に抑えて変更できるようにすることが重要です。実際、レイアウトマネージャーは、オブジェクト指向設計の信条の1つである、さまざまな概念をカプセル化する例を示しています。これは、多くのデザインパターンの基本的なテーマでもあります。

JSPは、レイアウトのカプセル化を直接サポートしていないため、通常、同じ形式のWebページはレイアウトコードを複製します。たとえば、図1は、ヘッダー、フッター、サイドバー、およびメインコンテンツセクションを含むWebページを示しています。

図1に示すページのレイアウトは、HTMLテーブルタグを使用して実装されています。

例1.コンテンツを含める

JSPテンプレート  
   
<%@ include file = "sidebar.html"%>
<%@ include file = "header.html"%>
<%@ include file = "introduction.html"%>
<%@ include file = "footer.html"%>

上記の例では、コンテンツはJSPincludeディレクティブに含まれています。これにより、ページ自体を変更せずに、含まれるファイルを変更することで、ページのコンテンツを変更できます。ただし、レイアウトはハードコーディングされているため、レイアウトを変更するにはページを変更する必要があります。ウェブサイトに同じフォーマットの複数のページがある場合(これは一般的です)、単純なレイアウト変更でもすべてのページを変更する必要があります。

レイアウト変更の影響を最小限に抑えるには、コンテンツに加えてレイアウトを含めるためのメカニズムが必要です。そうすれば、レイアウトとコンテンツの両方を、それらを使用するファイルを変更せずに変更できます。そのメカニズムがJSPテンプレートです。

テンプレートの使用

テンプレートは、パラメータ化されたコンテンツを含むJSPファイルです。この記事で説明したテンプレートは、カスタムタグのセットで実装されていますtemplate:gettemplate:puttemplate:inserttemplate:get図1に示したフォーマットでウェブページを生成する例2.Aに示すように、タグは、パラメータ化されたコンテンツにアクセスします。

例2.a. テンプレート

< template:get name = "title" />
   
< template:get name = "header" />

例2.aはtemplate:getincludeディレクティブの代わりに使用することを除いて、例1とほぼ同じです。どのようにtemplate:get機能するかを調べてみましょう。

template:get指定された名前のJavaBeanをリクエストスコープから取得します。Beanには、に含まれているWebコンポーネントのURI(Uniform Resource Identifier)が含まれていtemplate:getます。たとえば、例2.aにリストされているテンプレートでは、リクエストスコープで指定されたBeanからtemplate:getURIを取得します。続いて、が含まれます。header.htmlheadertemplate:getheader.html

template:putBeanをリクエストスコープに入れ、後でtemplate:get。によって取得されます。テンプレートはに含まれていtemplate:insertます。例2.bは、putおよびinsertタグの使用法を示しています。

例2.b. 例2.aのテンプレートを使用する

   
    template = "/articleTemplate.jsp">を挿入します
    
     put name = "title" content = "Templates" direct = "true" />
     
      put name = "header" content = "/ header.html" />
      
       put name = "sidebar" content = "/ sidebar.jsp" />
       
        put name = "content" content = "/ Introduction.html" />
        
         put name = "footer" content = "/ footer.html" />
        
       
      
     
    
   

insert開始タグは、この場合、テンプレートは、実施例2.a.節に記載されている、含まれるべきテンプレートを指定します 各putタグはリクエストスコープにBeanを格納し、insert終了タグにはテンプレートが含まれます。その後、テンプレートは上記のようにBeanにアクセスします。

direct属性には、のために指定することができますtemplate:putdirectがに設定されている場合true、タグに関連付けられているコンテンツはに含まれませんtemplate:getが、暗黙のout変数に直接出力されます。たとえば、例2.bでは、タイトルコンテンツ(JSPテンプレート)がウィンドウタイトルに使用されています。

同じ形式の複数のページを含むWebサイトには、例2.aにリストされているような1つのテンプレートと、そのテンプレートを使用する例2.bなどの多くのJSPページがあります。フォーマットが変更された場合、変更はテンプレートに制限されます。

テンプレートと一般的なコンテンツを含めることのもう1つの利点は、モジュラー設計です。たとえば、例2.bにリストされているJSPファイルにはheader.html、最終的に、例2.cにリストされているが含まれています。

例2.c. header.html

   

Because header.html is included content, it does not have to be replicated among pages that display a header. Also, although header.html is an HTML file, it does not contain the usual preamble of HTML tags such as or because those tags are defined by the template. That is, because the template includes header.html, those tags should not be repeated in header.html.

Note: JSP provides two ways to include content: statically, with the include directive, and dynamically, with the include action. The include directive includes the source of the target page at compile time and is equivalent to C's #include or Java's import. The include action includes the target's response generated at runtime.

Like the JSP include action, templates include content dynamically. So, although the JSP pages in Example 1 and Example 2.b are functionally identical, the former statically includes content, whereas the latter dynamically includes it.

Optional content

All template content is optional, which makes a single template useful to more Webpages. For example, Figure 2.a and Figure 2.b show two pages -- login and inventory -- that use the same template. Both pages have a header, footer, and main content. The inventory page has an edit panel (which the login page lacks) for making inventory changes.

Below, you'll find the template shared by the login and inventory pages:

 ... 
   
name='editPanel'/>
...

The inventory page uses the template listed above and specifies content for the edit panel:

   ... 
    ...  

In contrast, the login page does not specify content for the edit panel:


  

Because the login page does not specify content for the edit panel, it's not included.

Role-based content

Web applications often discriminate content based on a user's role. For example, the same JSP template, which includes the edit panel only when the user's role is curator, produces the two pages shown in Figures 3.a and 3.b.

The template used in Figures 3.a and 3.b uses template:get's role attribute:

 ... 
   
     ... 
     ... 
    
role='curator'/>
...

The get tag includes content only if the user's role matches the role attribute. Let's look at how the tag handler for template:get uses the role attribute:

public class GetTag extends TagSupport { private String name = null, role = null; ... public void setRole(String role) { this.role = role; } ... public int doStartTag() throws JspException { ... if(param != null) { if(roleIsValid()) { // include or print content ... } } ... } private boolean roleIsValid()  } 

Implementing templates

The templates discussed in this article are implemented with three custom tags:

  • template:insert
  • template:put
  • template:get

The insert tag includes a template, but before it does, put tags store information -- a name, URI, and Boolean value specifying whether content should be included or printed directly -- about the content the template includes. template:get, which includes (or prints) the specified content, subsequently accesses the information.

template:put stores beans in request scope but not directly because if two templates use the same content names, a nested template could overwrite the enclosing template's content.

To ensure that each template has access only to its own information, template:insert maintains a stack of hashtables. Each insert start tag creates a hashtable and pushes it on the stack. The enclosed put tags create beans and store them in the newly created hashtable. Subsequently, get tags in the included template access the beans in the hashtable. Figure 4 shows how the stack is maintained for nested templates.

Each template in Figure 4 accesses the correct footer; footer.html for template_1.jsp and footer_2.html for template_2.jsp. If the beans were stored directly in request scope, step 5 in Figure 4 would overwrite the footer bean specified in step 2.

Template tag implementations

The remainder of this article examines the implementation of the three template tags: insert, put, and get. We begin with sequence diagrams, starting with Figure 5. It illustrates the sequence of events for the insert and put tags when a template is used.

If a template stack does not already exist, the insert start tag creates one and places it in request scope. A hashtable is subsequently created and pushed on the stack.

put開始タグはPageParameterBeanを作成し、囲んでいるinsertタグによって作成されたハッシュテーブルに格納されます。

挿入endタグにはテンプレートが含まれています。テンプレートはgetタグを使用して、タグによって作成されたBeanにアクセスしputます。テンプレートが処理された後、insert開始タグによって作成されたハッシュテーブルがスタックからポップされます。

図6に、のシーケンス図を示しますtemplate:get

テンプレートタグリスト

テンプレートタグのタグハンドラーの実装は簡単です。例3.aは、InsertTagクラス(のタグハンドラー)を示していますtemplate:insert

例3.a. InsertTag.java