Web アプリケーションに Velocity を導入する

テンプレートエンジンが Web アプリケーションで活かされる例を見てみましょう。

以前 Customers というプロジェクトを作りました。最初は ServletJSP で作り、それを Struts で書き変えました。今度はそこに Velocity を導入してみます。

ライブラリの追加

/WEB-INF/lib に以下の jar を追加します。

  • commons-collections-3.2.1.jar
  • commons-lang-2.6.jar
  • commons-logging-1.1.1.jar*1
  • velocity-1.7.jar*2
  • velocity-tools-2.0.jar

velocity-toolbox.xml の作成

/WEB-INF 直下に以下の内容で velocity-toolbox.xml を作成します。

<?xml version="1.0"?>
<toolbox>
  <tool>
    <key>math</key>
    <scope>application</scope>
    <class>org.apache.velocity.tools.generic.MathTool</class>
  </tool>

  <tool>
    <key>link</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.StrutsLinkTool</class>
  </tool>

  <tool>
    <key>text</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.MessageTool</class>
  </tool>

  <tool>
    <key>errors</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.ErrorsTool</class>
  </tool>

  <tool>
    <key>messages</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.ActionMessagesTool</class>
  </tool>

  <tool>
    <key>form</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.FormTool</class>
  </tool>

  <tool>
    <key>tiles</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.TilesTool</class>
  </tool>

  <tool>
    <key>validator</key>
    <scope>request</scope>
    <class>org.apache.velocity.tools.struts.ValidatorTool</class>
  </tool>
</toolbox>

velocity.properties の作成

/WEB-INF 直下に以下の内容で velocity.properties を作成します。

input.encoding = utf-8
output.encoding = utf-8

web.xml の修正

web.xml に Velocity を servlet として登録します。

<?xml version="1.0" encoding="utf-8"?>
<web-app
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  id="WebApp_ID" version="3.0">

  <display-name>Customers</display-name>

  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>velocity</servlet-name>
    <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
    <init-param>
      <param-name>org.apache.velocity.toolbox</param-name>
      <param-value>/WEB-INF/velocity-toolbox.xml</param-value>
    </init-param>
    <init-param>
      <param-name>org.apache.velocity.properties</param-name>
      <param-value>/WEB-INF/velocity.properties</param-value>
    </init-param>
    <load-on-startup>10</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>velocity</servlet-name>
    <url-pattern>*.vm</url-pattern>
  </servlet-mapping>

  <filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>jp.mydns.akanekodou.filter.EncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <param-name>language</param-name>
      <param-value>ja</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <welcome-file-list>
    <welcome-file>init.do</welcome-file>
  </welcome-file-list>

</web-app>

init.vm の作成

init.jsp の代わりに新たに init.vm を作成します。

<!doctype html>
<html>
<head>
  <title>顧客マスタ一覧</title>
</head>
<body>
  <h1>顧客マスタ一覧</h1>
  <table border>
    <thead>
      <tr><th>ID</th><th>顧客名</th><th>連絡先</th></tr>
    </thead>
    <tbody>
      #foreach ($cust in $custlist)
      <tr>
        <td>$cust.id</td>
        <td>$cust.name</td>
        <td>$cust.phone</td>
      </tr>
      #end
    </tbody>
  </table>
</body>
</html>

ここまで来るとほぼ HTML ですね。見た目も想像がつきそうです。これが Velocity を導入する利点です。

CustomerDAO.java の修正

CustomerList を使うやり方は冗長になるので、List<Customer> に型を変更します。

package jp.mydns.akanekodou.dao;

import javax.sql.DataSource;
import java.sql.*;

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

import jp.mydns.akanekodou.model.*;

public class CustomerDAO {
    private static final String SQL = "SELECT * FROM 顧客マスタ";

    private DataSource source;

    public CustomerDAO() {
        source = DaoUtil.getSource();
    }

    public List<Customer> all() {
        List<Customer> list = new ArrayList<Customer>();

        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            con = source.getConnection();
            stmt = con.createStatement();
            rs = stmt.executeQuery(SQL);

            while(rs.next()) {
                list.add(getCustomer(rs));
            }
        } catch(SQLException se) {
            se.printStackTrace();
        } finally {
            try {
                if(rs != null) rs.close();
                if(stmt != null) stmt.close();
                if(con != null) con.close();
            } catch(SQLException se) {
            }
        }

        return list;
    }

    private Customer getCustomer(ResultSet rs) throws SQLException {
        Customer c = new Customer();

        c.setId(rs.getInt("顧客ID"));
        c.setName(rs.getString("顧客名"));
        c.setPhone(rs.getString("連絡先"));

        return c;
    }
}

InitAction.java の修正

上記の修正に伴う型の変更です。

package jp.mydns.akanekodou;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import java.util.List;

import jp.mydns.akanekodou.model.Customer;
import jp.mydns.akanekodou.dao.CustomerDAO;

public class InitAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<Customer> custlist = new CustomerDAO().all();
        request.setAttribute("custlist", custlist);
        return mapping.getInputForward();
    }
}

struts-config.xml の修正

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>

  <action-mappings>
    <action path="/init" type="jp.mydns.akanekodou.InitAction" input="/init.vm" />
  </action-mappings>

</struts-config>

設定ファイルの作成や書き換えなどが大変ですが、View がすっきりしたことが大きな成果です。ここまですっきりすれば、プログラミングの知識が少ない人でも見た目が想像できるので分業がやりやすいですね。

*1:Struts に含まれる commons-logging-1.0.4.jar と入れ替えてください。

*2:velocity-1.7-dep.jar は既に Struts に含まれるパッケージとの重複がありますので使いません。