Home日記コラム書評HintsLinks自己紹介
 

Tips: JSF

Hints: JSF

JSF に関するどうでもいいことから訳の分からないことまで、 メモ的に書いてみることにする。

Spring Framework との連携 (2)

JSF + Spring + Hibernate の連携で、 applicationContext.xml で hibernate を使うための facade を設定している場合に、 JSF からこれを使うにはどうすればいいか。

(2006-01-29)

Spring Framework との連携

Spring Framework と JSF を連携する場合、 applicationContext.xml に bean の定義を書いておき、 JSF の managed bean としては定義しない、 という使い方をすることがある。

Eclipse で EclipseHTMLEditor を使って編集する場合、 spring の定義の bean まできっちり見てエラーを判定してくれる。 つまり、 applicationContext.xml の bean 定義の中には、 JSF からアクセスしたいプロパティの property と、 呼び出したいメソッドの lookup-method を書いておく必要がある。 これらが書いてないと、 未定義の property や method ということで編集画面でエラーが発生する。

(2006-01-05)

name とvalue のペアを画面上に表示する

name と value のペアを画面上に表示したい。 まず、単に表示するだけならこんな感じ。

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="x" %>

<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>JSF で name-value ペアを表示: サンプル</title>
  </head>

  <body>
    <f:view>

      <h:panelGrid id="showgrid" columns="2">

      <h:panelGrid id="場所情報" columns="2">
        <f:facet name="header">
          <h:outputText value="場所情報"/>
        </f:facet>

        <h:outputText value="住所" />
        <h:panelGroup>
          <h:outputText value="東京都永田区某所1-2-3" />
        </h:panelGroup>

        <h:outputText value="電話" />
        <h:panelGroup>
          <h:outputText value="03-1234-5678" />
        </h:panelGroup>

        <h:outputText value="E-mail" />
        <h:panelGroup>
          <h:outputLink value="mailto:foo@hogehoge.go.jp">
            <f:verbatim>foo@hogehoge.go.jp</f:verbatim>
          </h:outputLink>
        </h:panelGroup>

      </h:panelGrid>

    </f:view>
  </body>
</html>

実際に画面に出すとこんな感じ。

目標の画面

これだと拡張性も何もあったものではないが、 ここから、表示する内容を dynamic にするというのが目標になる。 まず、これに対して、 ManagedBean を定義する。

creating SampleBean (Eclipse)

/**
 * 
 */
package com.phinloda.example;

/**
 * @author mai
 *
 */
public class SampleBean {
    private String title;

    /**
     * @return Returns the title.
     */
    public String getTitle() {
        return title;
    }

    /**
     * @param title The title to set.
     */
    public void setTitle(String title) {
        this.title = title;
    }

}

タイトルだけまず確認するということで、 ここまでで、ManagedBean の定義を、 faces-config.xml に書く。

  <managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.SampleBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
     <property-name>title</property-name>
     <property-class>java.lang.String</property-class>
     <value>場所情報(faces-config.xml)</value>
    </managed-property>
  </managed-bean>

先ほどの jsp ファイルの、

      <h:panelGrid id="場所情報" columns="2">
        <f:facet name="header">
          <h:outputText value="場所情報"/>
        </f:facet>

こうなっていた箇所を、次のように変更する。

      <h:panelGrid id="場所情報" columns="2">
        <f:facet name="header">
          <h:outputText value="#{myBean.title}"/>
        </f:facet>

はて、画面表示が変わらない。 ヘッダーの表示が、 場所情報(faces-config.xml) になってほしいのだが。 ということで、

        <h:panelGrid id="場所情報" columns="2">

この箇所を、

        <h:panelGrid id="場所情報1" columns="2">

としてみる。 つまり、id を変更してみる。 これで、期待通りの表示に変わる。 id が同じものはキャッシュされる仕組みになっているのだろうか?

まあそれはおいといて、 次に、住所だが、そのまま MyBean に埋め込んでも意味がないので、 まず、キーと値のペアを表現するためのクラスをつくる。

/**
 * 
 */
package com.phinloda.example;

/**
 * @author mai
 *
 */
public class NameValuePair {
    public String name;

    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }

    /**
     * @param name The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }
    
    public String value;

    /**
     * @return Returns the value.
     */
    public String getValue() {
        return value;
    }

    /**
     * @param value The value to set.
     */
    public void setValue(String value) {
        this.value = value;
    }

}

実は NameValuePair というクラスは、既にいくつか存在している。 例えば、Jakarta Commons の HttpClient には、 org.apache.commons.httpclient.NameValuePair というクラスがある。 これをそのまま使っても全然構わない。 というか、 このようなクラスがあちこちに存在するというのはどうも気に入らない。 とはいっても、 単に name と value の組を使いたいだけの理由で httpclient を使うというのも、 何かおかしいような気もする。 とりあえず、今回は拡張する予定もあるので自作しているが、 httpclient の NameValuePair には、 equals と hashCode が実装されているので、 そのあたり「何のこと?」という人は、見ておいた方がいい。

とにかく、SampleBean クラスには、次のコードを追加する。

	private NameValuePair address;

	/**
	 * @return Returns the address.
	 */
	public NameValuePair getAddress() {
		return address;
	}

	/**
	 * @param address The address to set.
	 */
	public void setAddress(NameValuePair address) {
		this.address = address;
	}

ManagedBean は、次のように変更する。

  <managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>com.phinloda.sample.MyBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
     <property-name>title</property-name>
     <property-class>java.lang.String</property-class>
     <value>場所情報</value>
    </managed-property>
    <managed-property>
     <property-name>address</property-name>
     <property-class>com.phinloda.example.NameValuePair</property-class>
     <value>#{sampleAddressBean}</value>
    </managed-property>
  </managed-bean>

  <managed-bean>
    <managed-bean-name>sampleAddressBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.NameValuePair</managed-bean-class>
    <managed-bean-scope>none</managed-bean-scope>
    <managed-property>
     <property-name>name</property-name>
     <property-class>java.lang.String</property-class>
     <value>住所(xml)</value>
    </managed-property>
    <managed-property>
     <property-name>value</property-name>
     <property-class>com.lang.String</property-class>
     <value>東京都永田区某所1-2-3(xml)</value>
    </managed-property>
  </managed-bean>

title の value は faces-config.xml の内容が表示されたことを確認したので、 「場所情報」に戻した。 これで、jsp の次の箇所、

        <h:outputText value="住所" />
        <h:panelGroup>
          <h:outputText value="東京都永田区某所1-2-3" />
        </h:panelGroup>

これを次のように変更する。

        <h:outputText value="#{myBean.address.name}" />
        <h:panelGroup>
          <h:outputText value="#{myBean.address.value}" />
        </h:panelGroup>

何となく JSF っぽくなってきた。 さらに、電話番号、 メールアドレスを表示できるように、 SampleBean に属性を追加する。

	private NameValuePair telephone;

	/**
	 * @return Returns the telephone.
	 */
	public NameValuePair getTelephone() {
		return telephone;
	}

	/**
	 * @param telephone The telephone to set.
	 */
	public void setTelephone(NameValuePair telephone) {
		this.telephone = telephone;
	}
	
	private NameValuePair email;

	/**
	 * @return Returns the email.
	 */
	public NameValuePair getEmail() {
		return email;
	}

	/**
	 * @param email The email to set.
	 */
	public void setEmail(NameValuePair email) {
		this.email = email;
	}

この段階で、 faces-config.xml は、このようになっている。

  <managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.SampleBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
     <property-name>title</property-name>
     <property-class>java.lang.String</property-class>
     <value>場所情報</value>
    </managed-property>
    <managed-property>
     <property-name>address</property-name>
     <property-class>com.phinloda.example.NameValuePair</property-class>
     <value>#{sampleAddressBean}</value>
    </managed-property>
    <managed-property>
     <property-name>telephone</property-name>
     <property-class>com.phinloda.example.NameValuePair</property-class>
     <value>#{sampleTelephoneBean}</value>
    </managed-property>
    <managed-property>
     <property-name>email</property-name>
     <property-class>com.phinloda.example.NameValuePair</property-class>
     <value>#{sampleEmailBean}</value>
    </managed-property>
  </managed-bean>

  <managed-bean>
    <managed-bean-name>sampleAddressBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.NameValuePair</managed-bean-class>
    <managed-bean-scope>none</managed-bean-scope>
    <managed-property>
     <property-name>name</property-name>
     <property-class>java.lang.String</property-class>
     <value>住所</value>
    </managed-property>
    <managed-property>
     <property-name>value</property-name>
     <property-class>java.lang.String</property-class>
     <value>東京都永田区某所1-2-3</value>
    </managed-property>
  </managed-bean>
  <managed-bean>
    <managed-bean-name>sampleTelephoneBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.NameValuePair</managed-bean-class>
    <managed-bean-scope>none</managed-bean-scope>
    <managed-property>
     <property-name>name</property-name>
     <property-class>java.lang.String</property-class>
     <value>電話番号</value>
    </managed-property>
    <managed-property>
     <property-name>value</property-name>
     <property-class>java.lang.String</property-class>
     <value>03-0000-0000</value>
    </managed-property>
  </managed-bean>
  <managed-bean>
    <managed-bean-name>sampleEmailBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.NameValuePair</managed-bean-class>
    <managed-bean-scope>none</managed-bean-scope>
    <managed-property>
     <property-name>name</property-name>
     <property-class>java.lang.String</property-class>
     <value>Email</value>
    </managed-property>
    <managed-property>
     <property-name>value</property-name>
     <property-class>java.lang.String</property-class>
     <value>phinloda@phinloda.com</value>
    </managed-property>
  </managed-bean>

これで画面は次のように表示されている。

sample 4

このように、 属性ごとにアクセサを用意するというのは芸がないので、 List を一つだけ持つことにして、 そこに NameValuePair のオブジェクトを格納するようにする。

/**
 * 
 */
package com.phinloda.example;

import java.util.ArrayList;
import java.util.List;

/**
 * @author mai
 *
 */
public class SampleBean {
	public SampleBean() {
		nameValueList = new ArrayList();
	}
	
	private String title;

	/**
	 * @return Returns the title.
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * @param title The title to set.
	 */
	public void setTitle(String title) {
		this.title = title;
	}

	private List nameValueList;

	/**
	 * @return Returns the nameValueList.
	 */
	public List getNameValueList() {
		return nameValueList;
	}

	/**
	 * @param nameValueList The nameValueList to set.
	 */
	public void setNameValueList(List nameValueList) {
		this.nameValueList = nameValueList;
	}
	

}

faces-config.xml の定義は、次のように変更する。

  <managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>com.phinloda.example.SampleBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>

    <managed-property>
     <property-name>title</property-name>
     <property-class>java.lang.String</property-class>
     <value>場所情報</value>
    </managed-property>

    <managed-property>
      <property-name>nameValueList</property-name>
      <property-class>java.util.List</property-class>
      <list-entries>
        <value-class>com.phinloda.example.NameValuePair</value-class>
        <value>#{sampleAddressBean}</value>
        <value>#{sampleTelephoneBean}</value>
        <value>#{sampleEmailBean}</value>
      </list-entries>
    </managed-property>

  </managed-bean>

java.util.List の内容を list-entries で指定することに注意。 あとは、 sample1.jsp を修正して、 リストの中身を見るようにする。 その前に、E-mail の場合だけ、リンクにしたいので、 NameValuePair を extends して EmailBean クラスを作っておく。

/**
 * 
 */
package com.phinloda.example;

/**
 * @author mai
 *
 */
public class EmailBean extends NameValuePair {

}

faces-config.xml の、 sampleEmailBean の定義のところで、

    <managed-bean-class>com.phinloda.example.NameValuePair</managed-bean-class>

となっている箇所を、次のように変更する。

    <managed-bean-class>com.phinloda.example.EmailBean</managed-bean-class>

sample1.jsp は、このようにする。

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="x" %>

<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>JSF で name-value ペアを表示: サンプル</title>
  </head>

  <body>
    <f:view>

      <h:panelGrid id="showgrid" columns="2">

        <h:panelGrid id="場所情報" columns="2">
          <f:facet name="header">
            <h:outputText value="#{myBean.title}"/>
          </f:facet>

          <h:dataTable id="所在地情報一覧"
            value="#{myBean.nameValueList}"
            var="item">

            <h:column>
              <h:outputText id="nameLabel" value="#{item.name}"/>
            </h:column>

            <h:column>

              <h:outputText id="textType1"
                rendered="#{item.class.name != 'com.phinloda.example.EmailBean'}"
                value="#{item.value}"/>

              <h:outputLink id="linkType"
                rendered="#{item.class.name == 'com.phinloda.example.EmailBean'}"
                value="mailto:#{item.value}">
                <h:outputText value="#{item.value}"/>
              </h:outputLink>

            </h:column>

          </h:dataTable>

        </h:panelGrid>

      </h:panelGrid>

    </f:view>
  </body>
</html>

java.util.List の要素を順に処理するために、 h:dataTable を使っている。 例のように value に List のプロパティを指定して、 取り出した各要素を、その後で参照するための名前を var で指定する。 ここでは、getList(0), getList(1), … で取り出した各要素が、 item という名前で参照できるようにしている。

各要素に対応した item は、 NameValuePair なので、 name と value を取り出して表示すればいいのだが、 EmailBean クラスのときだけ、outputLink を使いたい。 その振り分けに、rendered を使っている。 次の箇所。

<h:outputText id="textType1"
  rendered="#{item.class.name != 'com.phinloda.example.EmailBean'}"
  value="#{item.value}"/>

<h:outputLink id="linkType"
  rendered="#{item.class.name == 'com.phinloda.example.EmailBean'}"
  value="mailto:#{item.value}">
  <h:outputText value="#{item.value}"/>
</h:outputLink>

これはいまいち面白くない書き方なのだが、 item.class.name というのが、 Java 的には item.getClass().getName() を呼び出している感じで、 その結果戻ってきた文字列を 'com.phinloda.example.EmailBean' と比較して、 一致しなかったときにはテキストのみの表示、 一致したときには、リンク表示としている。 このような書き方をすると、 後で場合が増えたときにややこしくなりそうな気がするのだが。 とりあえず、これで表示結果は目的の状態になった。 なお、 outputLink の value を指定するところで、 mailto:#{item.value} と書いてあるが、 このように文字列を前後に付加できる。

(2005-10-13)