目次へ

5. Maven による実アプリケーション開発

2006.07.24 株式会社四次元データ 鈴木 圭

5.5. Web アプリケーション部分の作成

ここからはメインとなる Web アプリケーション部分の作成に入ります。Web アプリケーション・フレームワークとして Struts を使用します。

また、src/main/webapp/index.jsp は不要なので削除しておきます。

5.5.1 dependency の追加

Web アプリケーション部分では servlet-api と Struts のライブラリが必要なので、以下のライブラリの情報を pom.xml に記述します。注意として、servlet-api は実行環境で提供されるので、スコープに「provided」を指定します:

groupId artifactId version scope
javax.servlet servlet-api 2.4 provided
struts struts 1.2.4 compile (default)

pom.xml は次のようになります:

<project ...>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>struts</groupId>
      <artifactId>struts</artifactId>
      <version>1.2.4</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>

Struts のバージョンに若干古い値を指定しているのは、現時点で Maven のセントラルリポジトリに存在する最新バージョンが Struts 1.2.4 であるためです。今回は多少古いバージョンでも問題ありませんので、あるものをそのまま使うことにしました。もし「最新バージョンを使いたい」ということであれば、「3.1.4. ライブラリをローカルリポジトリにインストールする」を参照しながら、手作業でインストールしてください。

5.5.2. 中心部分となる Action と JSP の作成

全体としてやるべきことは色々あるのですが、とりあえずチャット機能の中心となる部分から作成し、後から入力の検査や、エンコーディングの処理などを追加することにしましょう。

チャット機能の中心となるのは、以下に挙げる 3 つの Action と JSP ファイルです:

com.example.webchat.webapp.struts.action.SaveMessageAction 与えられた発言データを保存する。
com.example.webchat.webapp.struts.action.ShowMessagesAction 表示すべき発言データを request に設定する。
src/main/webapp/WEB-INF/jsp/ShowMessages.jsp request に設定されている発言データを表示する。

ShowMessagesAction と SaveMessageAction はそれぞれ ShowMessages.do と SaveMessage.do というリクエストに対して実行されるようにマッピングします。

以下に SaveMessageAction, ShowMessagesAction, ShowMessages.jsp の関係を示します:

SaveMessageAction, ShowMessagesAction, ShowMessages.jsp の関係

利用者からのアクセスが行われると、ShowMessagesAction によって表示すべき発言データが request に設定 (HttpServletRequest#setAttribute()) され、ShowMessages.jsp に forward されます。ShowMessages.jsp では、request に設定されている発言データの表示を行います。

また、ShowMessages.jsp では情報の「更新」リンクと、発言フォームの表示も行います。情報の「更新」リンクからは ShowMessagesAction に遷移します。発言フォームにより発言が行われたときは、SaveMessageAction により発言データが保存されたあと、ShowMessagesAction に処理が移ります。

それでは一つひとつ作成していきましょう。

ShowMessagesAction の作成

ShowMessagesAction は、アプリケーションの利用者からのアクセスが行われたときに最初に実行される Action です。ShowMessagesAction では、保存されている全ての発言データを取得し、request に設定します (以下のソースコードでは import 文などは省略しています):

public class ShowMessagesAction extends Action
{
    public ActionForward execute(
            ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
    {
        MessageDao messageDao = MessageDaoOnMemoryImpl.getInstance();
        request.setAttribute("messages", messageDao.getAll());
        return mapping.findForward("success");
    }
}

発言データは messageDao.getAll() の戻り値 (MessageBean の配列) を "messages" という名前で関連付けることにします。

ShowMessages.jsp の作成

次は利用者とのインタフェースとなる ShowMessages.jsp を作成します。

ShowMessages.jsp では、発言用フォームと、それまでの発言内容の表示を行います。イメージとしては、次のようなインタフェースとなるように作成します:

少々長くなりますが、以下に ShowMessages.jsp の内容を示します:

<%@ page contentType="text/html; charset=Windows-31J" %>
<%@ taglib uri="http://struts.apache.org/tags-html"  prefix="html"  %>
<%@ taglib uri="http://struts.apache.org/tags-bean"  prefix="bean"  %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>

<html:html>
<head>
<title>webchat</title>
</head>
<body>

<h1>webchat</h1>

<hr />

<h2>発言フォーム</h2>

<html:form action="/SaveMessage">
<table>
<tr><th>名前</th>
    <td><html:text property="name" style="width:256" /></td></tr>
<tr><th>メッセージ</th>
    <td><html:text property="message" style="width:256" /></td></tr>
<tr><td colspan="2" align="right">
    <html:reset value="リセット" />
    <html:submit value="送信" /></td></tr>
</table>
</html:form>

<hr />

<h2>メッセージ</h2>

<html:link action="/ShowMessages">更新</html:link>

<table border="1" cellspacing="0" cellpadding="3">
<tr><th>Date</th>
    <th>Name</th>
    <th>Message</th></tr>
<logic:iterate id="message" name="messages" scope="request">
<tr><td><bean:write name="message" property="date" /></td>
    <td><bean:write name="message" property="name" /></td>
    <td><bean:write name="message" property="message" /></td></tr>
</logic:iterate>
</table>

</body>
</html:html>

発言用フォームでは、発言者の名前とメッセージをそれぞれ "name"、"message" というパラメータ名にしています。次に作成する SaveMessageAction では、"name" と "message" という名前のパラメータを取得するようにします。

また、チャットでの発言内容は MessageBean の配列として "messages" という名前で request に設定されているので、それを logic:iteration タグで一つずつ処理しています。

SaveMessageAction の作成

SaveMessageAction では、パラメータとして渡される発言データを保存し、ShowMessagesAction に forward します:

public class SaveMessageAction extends Action
{
    public ActionForward execute(
            ActionMapping mapping, ActionForm actionForm,
            HttpServletRequest request, HttpServletResponse response)
    {
        DynaActionForm form = (DynaActionForm)actionForm;
        Date       date       = new Date();
        String     name       = form.getString("name");
        String     message    = form.getString("message");
        MessageDao messageDao = MessageDaoOnMemoryImpl.getInstance();
        messageDao.save(new MessageBean(date, name, message));

        // 発言フォームの「名前」の値はそのまま残し, 「メッセージ」の値はクリアする.
        form.set("message", "");

        return mapping.findForward("success");
    }
}

一般的にチャットの利用者は、発言者名に前回入力した値をそのまま使うと思われるので、発言データを保存した後は、発言内容だけクリア (空の文字列を設定) しています。

5.5.3. とりあえず動作させてみる

ここまでできたら、とりあえず設定ファイルを記述して動作させてみましょう。

設定ファイルの類で必要となるのは次の 3 つです:

  • src/main/webapp/WEB-INF/web.xml
  • src/main/webapp/WEB-INF/struts-config.xml
  • src/main/resources/com/example/webchat/webapp/struts/Messages.properties

Struts を使用するので web.xml 以外に struts-config.xml が必要となります。また、ActionForm (DynaActionForm) を利用するので、Messages.properties ファイル (ファイル名は任意) も作成します。

web.xml の記述

web.xml はプロジェクトを作成したときに自動的に作成されているので、それを変更します。web.xml の内容は次のようになります:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>webchat</display-name>

  <servlet>
    <servlet-name>ActionServlet</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>
  </servlet>

  <servlet-mapping>
    <servlet-name>ActionServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

struts-config.xml の記述

次に struts-config.xml を作成します:

<?xml version="1.0" encoding="Windows-31J" ?>
<!DOCTYPE struts-config PUBLIC
  "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
  "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
  <form-beans>
    <form-bean name="MessageForm" type="org.apache.struts.action.DynaActionForm">
      <form-property name="name"    type="java.lang.String" />
      <form-property name="message" type="java.lang.String" />
    </form-bean>
  </form-beans>

  <action-mappings>
    <action path="/ShowMessages"
            type="com.example.webchat.webapp.struts.action.ShowMessagesAction">
      <forward name="success" path="/WEB-INF/jsp/ShowMessages.jsp" />
    </action>

    <action path="/SaveMessage" name="MessageForm" input="/ShowMessages.jsp"
            type="com.example.webchat.webapp.struts.action.SaveMessageAction">
      <forward name="success" path="/ShowMessages.do" />
    </action>
  </action-mappings>

  <message-resources parameter="com.example.webchat.webapp.struts.Messages" />
</struts-config>

発言フォームからの入力を受け取る ActionForm は動的に生成 (DynaActionForm を使用) することにします。また、Action のマッピングは前述の通り「ShowMessages.do → ShowMessagesAction」「SaveMessage.do → SaveMessageAction」となるように記述します。

最後に、ActionForm (DynaActionForm) を利用するので、/struts-config/message-resources 要素でメッセージリソースの指定も行います。

Messages.properties の作成

ActionForm (DynaActionForm) を使用するので、struts-config.xml ではメッセージリソースの指定を行いました。そこで、メッセージリソース用のファイル (Messages.properties) を作成します。

Maven では Messages.properties のようなリソースファイルは src/main/resources ディレクトリ以下に配置することになっているので、src/main/resources/com/example/webchat/webapp/struts ディレクトリを作成し、そこに Messages.properties ファイルを作成しましょう。

今回のサンプルでは (Validation は行わないので) メッセージリソース自体は使用しません。そのため、Messages.properties ファイルの内容は空で構いません。

ここまでの作業内容

これで、アプリケーションを動作させる準備はできました。色々なディレクトリで作業を行ったので、ここでプロジェクトの全体を確認しましょう。プロジェクトの src ディレクトリ以下は次のようになっています (作成または変更したファイルは強調表示してあります):

src
  └─main
      ├─java
      │  └─com
      │      └─example
      │          └─webchat
      │              ├─db
      │              │  ├─bean
      │              │  │      MessageBean.java
      │              │  │
      │              │  └─dao
      │              │      │  MessageDao.java
      │              │      │
      │              │      └─impl
      │              │              MessageDaoOnMemoryImpl.java
      │              │
      │              └─webapp
      │                  └─struts
      │                      └─action
      │                              SaveMessageAction.javaShowMessagesAction.java
      │
      ├─resources
      │  └─com
      │      └─example
      │          └─webchat
      │              └─webapp
      │                  └─struts
      │                          Messages.properties
      │
      └─webapp
          └─WEB-INF
              │  struts-config.xmlweb.xml
              │
              └─jsp
                      ShowMessages.jsp

プロジェクトのビルド

ここまでの作業を終えたら、プロジェクトをビルドします:

mvn package

上記コマンドを実行すると、target ディレクトリに諸々のファイルが作成されます。target/webchat.war がプロジェクトの最終成果物となりますが、開発中は target/webchat.war が展開された形式の target/webchat ディレクトリをアプリケーションのルートディレクトリとして実行するようにします。

Tomcat の server.xml の記述

これでアプリケーションを動作させる準備ができたので、Tomcat の server.xml にアプリケーションの情報を記述します:

<Server ...>
  ...
  <Service ...>
    ...
    <Engine ...>
      ...
      <Host ...>
        ...
        <Context path="/webchat" docBase="<プロジェクトのディレクトリ>/target/webchat" />
        ...
      </Host>
      ...
    </Engine>
    ...
  </Service>
  ...
</Server>

ここでの作業は Server/Service/Engine/Host の入れ子として Context 要素を追加することです。Context の属性 path にはブラウザからアクセスするときのパスとして「/webchat」を指定します。また、docBase 属性には「<プロジェクトのディレクトリ>/target/webchat」を指定します。

アプリケーションの動作を確認する

それでは Tomcat を実行し、「http://localhost:8080/webchat/ShowMessages.do」にアクセスしてみましょう。上手く起動できたなら、実際に操作して動作を確かめてみてください。とりあえず動作するものの、未入力項目があってもそのまま処理されてしまったり、日本語を正しく扱えないなどの不備も見つかると思います。ここからは、それらの問題に対処することで、アプリケーションの完成度を向上させていきたいと思います。

5.6. Web アプリケーション部分の改良

ここからは、アプリケーションの完成度を向上させるための作業に入ります。まずは、現状での問題点を整理しておきましょう。

5.6.1. 問題点の整理

現状での問題点として、以下の 2 点を挙げることができると思います:

  1. 日本語を正しく扱えない
  2. 未入力項目があっても、そのまま処理されてしまう

1 番目の問題に関しては、エンコーディングの設定を行うフィルタを作成することで対処します。2 番目の問題に関しては、Validation を行うことにより対処することができます。

今回は説明の量が多くなりすぎるのを避けるため、1 番目の問題だけ対処することにします。余裕のある方は 2 番目の問題の対処も行ってみてください。

5.6.2. 日本語を正しく扱えない問題の対処

「日本語を正しく扱えない問題」には javax.servlet.Filter を実装し、エンコーディングの指定を行うフィルタを作成することで対処しましょう。

CharacterEncodingFilter の作成

エンコーディングの指定を行うフィルタは「CharacterEncodingFilter」という名前で com.example.webchat.webapp.servlet.filter パッケージに作成しましょう。

今回のサンプルではエンコーディングは "Windows-31J" 固定、パラメータを含む HTTP の GET メソッドは発生しないという前提で、CharacterEncodingFilter を以下のように作成しました:

public class CharacterEncodingFilter implements Filter
{
    public void init(FilterConfig config)
    {
        // BLANK.
    }

    public void destroy()
    {
        // BLANK.
    }

    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain)
        throws ServletException, IOException
    {
        servletRequest.setCharacterEncoding("Windows-31J");

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

web.xml の変更

次に web.xml で CharacterEncodingFilter を使用するように設定します:

<web-app>
  <display-name>webchat</display-name>

  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>com.example.webchat.webapp.servlet.filter.CharacterEncodingFilter</filter-class>
  </filter>

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

  <servlet>
    ...
  </servlet>

  <servlet-mapping>
    ...
  </servlet-mapping>
</web-app>

変更箇所は、web-app 以下に filter 要素と filter-mapping 要素を追加することです。filter 要素では初期化パラメータとして文字のエンコーディング名を与えます。filter-mapping では CharacterEncodingFilter が全てのリクエストに対して適用されるように設定しています。

アプリケーションの動作確認

それでは、アプリケーションをビルドしなおして日本語を正しく扱えるか確認してみましょう。

Tomcat を停止してしまっている場合は、「mvn package」コマンドでアプリケーションをビルドしてから、Tomcat を起動してください。

Tomcat を起動したままの状態であれば、次のコマンドを実行してください:

mvn tomcat:stop package tomcat:start

mvn コマンドには 3 つの引数が指定されています。このように mvn コマンドには複数のゴールやフェイズを渡すことができます (それらは連続して実行されます)。

tomcat:stop と tomcat:start は先ほど導入した tomcat-maven-plugin が持つゴールであり、それぞれ Tomcat に配備されているアプリケーションの停止と起動を行います。2 番目の引数は、すでに紹介済みですが、アプリケーションのパッケージング (ここでは WAR ファイルの作成) を行うためのものです。

「mvn tomcat:stop package tomcat:start」というコマンドを実行すると、Tomcat 上のアプリケーションを停止、プロジェクトのビルド、そしてアプリケーションの起動という 3 つの作業を行うことができます。

Maven での Web アプリケーション開発は、「アプリケーションの修正」→「mvn tomcat:stop package tomcat:start」→「動作確認」というサイクルを回すことで、効率よく開発を進めることができます。

アプリケーションの起動ができたら、日本語が正しく扱えることを確認してください。

5.6.3. 完成

これで今回のサンプル、チャットアプリケーションは完成です。全ての作業を行ったプロジェクトは以下のリンクからダウンロードすることができます:

webchat.lzh

このプロジェクトを使用する場合は、setting.xml にサーバーの情報を記述する (「5.4.3. tomcat-maven-plugin の設定」参照) することを忘れないでください。また、Eclipse プロジェクトののファイル (.project ファイルなど) は含まれませんので、適宜「mvn eclipse:eclipse」コマンドで作成してください。

5.7. まとめ

今回は、具体的なアプリケーション開発ということで、Web ベースのチャットアプリケーションを作成しましたが、いかがだったでしょうか?

Maven を使用しない開発では、プロジェクトをスタートするために、ディレクトリ構成の決定や Ant のビルドファイルの作成、必要なライブラリの入手などの雑多な作業を行うことが常でした。しかし、今回のチャットアプリケーションの作成を通じて、Maven を使用することで、プロジェクト開始時に必要だった雑多な作業が大幅に軽減されることを分かっていただけたと思います。

特にライブラリに関しては、必要な情報を設定ファイルに記述しておくだけで、ライブラリの入手やコンパイル時のクラスパスの設定まで自動的に行われるため、手作業で 1 つ 1 つダウンロードするような手間はかかりません。また、設定ファイルにライブラリのバージョンも記述するため、プロジェクトがバージョン不明のライブラリに依存することが無い、ということも大きなメリットの 1 つです。

ここで、今回の作業工程を振り返ってみると、

  1. プロジェクトの作成
  2. 最小限の設定を行い Eclipse にプロジェクトをインポート
  3. データベース部分を作成
  4. Tomcat の準備
  5. tomcat-maven-plugin の導入
  6. 動作確認
  7. 日本語への対応
  8. 完成

という流れで作業を行いました。

今回は作成するアプリケーションが非常に小さなものだったため、設定ファイルの変更作業の割合が多くなってしまいましたが、もっと大きなアプリケーションを開発する場合は、「変更→動作環境への反映→動作確認→変更」というサイクルを何度も繰り返すことになると思います。その場合に、今回紹介した tomcat-maven-plugin を用いるなど、プラグインの力を借りれば、「変更→動作環境への反映」という作業もコマンド一つで行うことができます。

実際の開発では、「コンパイル→テスト→修正」や「ビルド→動作環境への反映→動作確認→修正」というサイクルをどれだけ早く回せるかで、開発スピードが違ってくると思います。Maven を利用すると、ほとんどの作業をコマンド一つで行うことができるため、開発における基本的なサイクルをすばやく回すことができます。

次回予告

次回は最終章ということで、Maven に関する Tips をまとめたいと思います。

↑このページの先頭へ

こちらもチェック!

PR
  • XMLDB.jp