Struts のサンプル(解説)

前に掲載した Struts のサンプルの解説です。軽く Q & A も付けます。
Q. 「せんせー、Struts って何ですかー ?」
A. StrutsJava フレームワークの代表格。2001 年に登場して以降、今なお現場で使われることが多い。ちなみに「フレームワーク」とは文字通り「枠組み」のことで、特に Web アプリケーションを作る際の一定の枠組みを提供することで、プログラミングの負担を軽減する目的で作られているものを指す。

Q. 「何で今更 Struts なの ?」
A. 先程も書いた通り、技術としては古いものの今なお現場で使われているという現状を踏まえ、簡単なサンプルを紹介することでフレームワークというものに触れるきっかけになればと思いチョイスしました。Struts 以外にもさまざまなフレームワークがあるので、好みのものを見つけていろいろ試すといいと思います。後ほど Wicket についても軽く紹介する予定。

ではソースの解説を。

4. web.xml について
(ソース省略)
Struts を使う際の web.xml は、基本こう書くものだと思っていただいて構いません。強いて言えば web-app 要素の id 属性は特に書かなくても動くと思います。

5. 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>
    <form-beans>
        <form-bean name="parseform" type="sample.framework.ParseActionForm" />
    </form-beans>

    <action-mappings>
        <action path="/action" name="parseform" type="sample.framework.ParseAction" input="/parse.jsp" />
    </action-mappings>
</struts-config>

最初の 2 行は固定です。struts-config.xml のルート要素は struts-config です。その中に form-beans 要素と action-mappings 要素を置きます。
form-beans 要素の中には form-bean 要素を置いて、ここで諸々の指定をします。name 属性でフォームの名前を指定します。これは後で説明する JSP が HTML に書きかえられるときに form 要素の name 属性の値になります。type 属性にはフォームの Bean となるクラスを指定します。
action-mappings 要素内の action 要素で、実際に処理をするクラスファイルなどの指定をします。path 属性に書いた値に ".do" をつけたものが、アクセス時の URL になります。この場合は "/action.do" ということになります。name には対応するフォームの名前を指定します。この場合は先程の "parseform" が対応しますので、それを指定します。type 属性は、処理を行うアクションクラスを指定します。input 属性は、元となる JSP ファイルを指定します。

6. index.jsp について
(ソース省略)
これは以前このブログでも取り上げた話題で、初めてサーブレットにアクセスしたときに URL に sessionid が付加されるのを防止するためだけのダミーです。

7. parse.jsp について

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html:html lang="true">
    <head>
        <title>進数変換</title>
        <link rel="stylesheet" href="parse.css" type="text/css">
    </head>
    <body>
        <html:form action="action.do" method="post">
            <fieldset>
                <legend>入力</legend>
                <html:text property="input" size="30" /><br>
                <html:radio property="inRadix" value="2">2 進</html:radio>
                <html:radio property="inRadix" value="8">8 進</html:radio>
                <html:radio property="inRadix" value="10">10 進</html:radio>
                <html:radio property="inRadix" value="16">16 進</html:radio>
            </fieldset>
            <p><html:submit value="変換" /></p>
            <fieldset>
                <legend>出力</legend>
                <html:text property="output" size="30" /><br>
                <html:radio property="outRadix" value="2">2 進</html:radio>
                <html:radio property="outRadix" value="8">8 進</html:radio>
                <html:radio property="outRadix" value="10">10 進</html:radio>
                <html:radio property="outRadix" value="16">16 進</html:radio>
            </fieldset>
        </html:form>
    </body>
</html:html>

Struts を使うときの JSP ファイルのルート要素には html:html を使うのが一般的です。2 行目の指定はこのような "html:" で始まる Struts 用のタグを使うための指定です。html:html の lang 属性に "true" を指定すると、HTML に書きかえられたとき html 要素に lang 属性が付け加えられます。
フォームには form の代わりに html:form を使います。このときの action 属性には、先程の struts-config.xml で設定したものだけが入れられます。他のものを入れるとエラーになります。HTML に書きかえられるとき、struts-config.xml で指定した name の値がそのまま name 属性の値として書き込まれます。
html:text は <input type="text"> に対応します。property は name 属性に対応し、プログラムでも参照します。size 属性はそのまま、他に value 属性で初期値を決めることもできます。
html:radio は <input type="radio"> に対応します。value 属性はそのラジオボタンをチェックして submit したときに送信される値です。
html:submit は <input type="submit"> に対応します。value 属性はボタンに表示される文字列です。

8. parse.css について
(ソース省略)
完全におまけです。fieldset 要素の枠線が間延びしないように width を指定しているだけです。jsp の中に style 要素を使って直接書き込んでも良いです。

9. Java クラスファイルについて
ここが肝です。それぞれについて解説します。

9-1. ParseActionForm.java について

package sample.framework;

import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class ParseActionForm extends ActionForm {
    private static final long serialVersionUID = 1L;

    private String input;
    private String output;
    private String inRadix;
    private String outRadix;

    public ParseActionForm() {
        inRadix = "10";
        outRadix = "10";
    }

    public String getInput() {
        return input;
    }
    public void setInput(String input) {
        this.input = input;
    }
    public String getOutput() {
        return output;
    }
    public void setOutput(String output) {
        this.output = output;
    }
    public String getInRadix() {
        return inRadix;
    }
    public void setInRadix(String inRadix) {
        this.inRadix = inRadix;
    }
    public String getOutRadix() {
        return outRadix;
    }
    public void setOutRadix(String outRadix) {
        this.outRadix = outRadix;
    }

    public void reset(ActionMapping mapping, HttpServletRequest request) {
        super.reset(mapping, request);
        try {
            request.setCharacterEncoding("utf-8");
        } catch(UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
    }
}

見てお分かりの通り、JavaBeans の書き方そのものです。JSP ファイルにあるフォームの各要素に対応するフィールドを用意し、それらの getter と setter を書きます。コンストラクタではフォームの初期化を行っています。ここではあらかじめ「10 進」のところにチェックが入るようにしておきたいので inRadix と outRadix に "10" を代入しています。reset メソッドは元となる ActionForm クラスに用意されているもので、それをオーバーライドします。やっていることは文字エンコードの設定です。一般には前回掲載した記事のように Filter を使いますが、今回は一個しかないので reset メソッドの中で指定しました。Filter を使う場合は reset メソッドは特に書く必要がありません。

9-2.ParseAction.java について

package sample.framework;

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;

public class ParseAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        ParseActionForm parseform = (ParseActionForm)form;
        String input = parseform.getInput();
        String output;
        int inRadix = Integer.parseInt(parseform.getInRadix());
        int outRadix = Integer.parseInt(parseform.getOutRadix());

        try {
            int tmp = Integer.parseInt(input, inRadix);
            output = Integer.toString(tmp, outRadix).toUpperCase();
        } catch(NumberFormatException nfe) {
            output = "Error!";
        }

        parseform.setOutput(output);

        return mapping.getInputForward();
    }
}

execute メソッドに実際の処理を書いていきます。
まず ParseActionForm のインスタンスを取得します。ここにはフォームに入力された情報が格納されていますので、それを getter で呼び出します。input と output は文字列のまま取り出します。inRadix と outRadix は、文字列で送られてきますが整数(int 型)に変換します。
実際の処理ですが、inRadix は input に入力された文字列が何進数かを決めるものです。これをもとに Integer.parseInt で整数に変換しますが、この際、入力した文字列に、指定した進数では使用されない数字や文字が入っていた場合 NumberFormatException が発生するため、エラーメッセージの表示に利用します。後は一旦整数に変換された入力を Integer.toString で希望する進数での表示に変換します。入力は大文字と小文字を区別しませんが、出力を大文字にするために toUpperCase を使用しました。後はこの結果を setter を使ってフォームにセットします。
最後の return 文は、別のページに遷移するとかでなければ通常この書き方で大丈夫です。

以上、ざっくりと解説を付けてみました。実際に動かしてみて、さらに理解を深めていただければ幸いです。フレームワークについては私もまだまだ勉強中で分からないことだらけですが、皆様がフレームワークに触れるきっかけを作れれば幸いです。