チュートリアル2(view と logic)

では、実際にコーディングしてみます。必要に応じて、Paraselene API を参照して下さい。

自動生成されるソースファイル

生成されたスケルトンソースファイルは以下のようになっています。

1tutorial1/Gate.javaサーブレット本体です。
web.xmlには、*.na の URL を Gate クラスで処理するよう指定されています。
2tutorial1/DummyPage.javaURL に遷移元ページが無い場合に処理されるクラスです。
3tutorial1/SuperPage.javalogic ディレクトリ下全てのクラスの親クラスです。
共通ルーチンを設けたい場合に使用します。
例えば、共通のエラーチェックがある、全ページ共通のヘッダやフッタを生成したい場合等がそれにあたります。
4tutorial1/Json.java非同期通信を行うためのクラスです。このチュートリアルでは説明しません。
※-json true を指定した場合のみ生成されます。
5tutorial1/logic/LoginHtml.javaログイン画面のロジックを記述します。
6tutorial1/logic/HelloHtml.javaHello 画面のロジックを記述します。
7tutorial1/view/LoginHtml.java開発者はこの中のソースを変更しないで下さい。
view ディレクトリのクラスは logic クラス直接の親クラスで、logic クラスと同じ数だけ存在します。
ここにあるソースは、モックアップ HTML をそのまま再現するための初期化ルーチンを持っています。
1度スケルトンを生成した後に HTML を変更し、再度スケルトンを生成しなおすと、このソースが新しく置き換わり、logic 側は変更されません。
8tutorial1/view/HelloHtml.java同上。
9tutorial1/base/PageType.java開発者はこのソースを変更しないで下さい。
各ページには PageID が設定され、プログラム中でページを指定する際には PageID を使用します。
このクラスはその定義を持ちます。その実体は Enum クラスです。
10tutorial1/base/PageLoader.java開発者はこのソースを変更しないで下さい。
Paraselene へ、tutorial1 で定義された各ページクラスインスタンスを提供する役目を持ちます。
このクラスはスレッド化され、バックグラウンドでページインスタンスを作り置きしています。
サイト利用者のアクセスに先行して、各ページインスタンスは既に複数個立ち上がり処理を待っている状態になっています。
11tutorial1/base/Json.java開発者はこのソースを変更しないで下さい。
非同期通信を行うためのクラスです。このチュートリアルでは説明しません。
※-json true を指定した場合のみ生成されます。

これらにjavadocを通せば、どのようなメソッドを持っているか把握しやすくなります。

logic クラスの内容

tutorial1/logic/LoginHtml.java の内容を見てみます。基本的に HelloHtml.java も同じ内容です。

tutorial1/logic/LoginHtml.java①
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
package tutorial1.logic;
 
import tutorial1.*;
import tutorial1.base.*;
import paraselene.*;
import paraselene.supervisor.*;
import paraselene.tag.*;
import paraselene.tag.attr.*;
import paraselene.tag.form.*;
import paraselene.tag.list.*;
import paraselene.tag.table.*;
 
/**
* login.html
*/
public class LoginHtml extends tutorial1.view.LoginHtml {
    /**
     * コンストラクタ。
     */
    public LoginHtml() {
        super();
        init();
    }
 
    /**
     * 初期化。
     */
    public void init(){
        super.init();
        // モックアップHTMLの内容と異なる内容で初期化したい場合は
        // super.init()の後に変更用の処理を記述して下さい。
        // このメソッドは上記コンストラクタの中から呼ばれます。
        // インスタンス発生はバックグラウンドで非同期に処理していますので
        // HTTPリクエスト発生よりずっと過去に初期化されます。
        // このため、ユーザーのトランザクションに応じた条件分岐を設けても
        // 意図した振る舞いになりません。
    }
 
    /**
    * 別名URI設定。nullを返すと別名は設定しません。
    * 最低4文字指定して下さい。
    * ".na"で終えると完全一致となりますが、".na"で無ければ先頭一致となります。
    * @return URI。
    */
    public String getAliasURI() {
        return null;
    }
 
    /**
     * 入力値の検証を行う。
     * このメソッドが呼ばれる際には必ずセッションが発生しています。
     * 入力値のエラーチェックや入力値に即した動作を記述します。
     * @param req リクエスト内容。
     * @param fw デフォルト遷移先。
     * @exception PageException 処理の継続が不可能(ブラウザには500を返す)。
     */
    public Forward inputMain( RequestParameter req, Forward fw ) throws PageException {
        // fwにはモックアップと同じ遷移先が設定されています。
        // 別の遷移先としたい場合は、新しくForwardインスタンスを生成して
        // リターンして下さい。
        return fw;
    }
 
    /**
     * 出力情報の設定を行う。
     * @param from 遷移元ページ。直接呼ばれている場合はnullです。
     * @param req リクエスト内容。
     * @return 出力ページ。
     * nullを返すとthisをリターンしたのと同じ扱いにされます。
     * @exception PageException 処理の継続が不可能(ブラウザには500を返す)。
     */
    public Page outputMain( Page from, RequestParameter req ) throws PageException {
        // 出力内容を設定します。
        // 初期化したい場合は、init()をコールして下さい。
        return this;
    }
}

このうち、主な変更箇所は inputMain() と outputMain() になります。

リクエスト処理

例えば、ブラウザにはログイン画面が表示されており、この時ログインボタンを押下すると以下のように処理がなされます。

  1. Web サーバーへ HTTP リクエスト発生。
  2. Gate クラスが URL を検証し、遷移元ページを特定します。ここでは LoginHtml クラスがそれに当たります。
    遷移元ページがフォームを持っていれば、 HTTP リクエストの値を各コントロールに当て嵌めます。
  3. LoginHtml クラスの inputMain() が呼び出されます。
    この時、引数の req にはリクエストパラメータやセッション情報が格納されています。ただし、ほとんどのリクエストパラメータは this インスタンスに設定されているので、それを確認する事を推奨します。
    引数の fw には、 HelloHtml クラスの PageID が設定されており、それをそのままリターンする事で HelloHtml の呼び出しを行っています。
  4. Gate クラスは inputMain() の戻り値より、遷移先ページを特定します。ここでは HelloHtml クラスがそれに当たります。
  5. HelloHtml クラスの outputMain() が呼び出されます。
    この時、from には LoginHtml クラスインスタンス(inputMain を処理したインスタンス)が格納されています。
    reqはinputMain() の引数と同じインスタンスです。ただし、ほとんどのリクエストパラメータは from インスタンスに設定されているので、それを参照する事を推奨します。
    状況に応じて this のタグの内容を変更します。
  6. Gate は outputMain のリターン値(HelloHtml クラスインスタンス)から HTML 出力を得て、それをブラウザに返します。

この処理フローを念頭にコーディングを行っていきます。

コーディング

では、このサイトの動作仕様を以下のようにします。

  • ログイン画面は、login.na でアクセス可能とする。
  • ログイン画面からログインする時、ユーザーIDとパスワードは必須入力項目とする。
  • ユーザーIDは何でも良い事とし、パスワード "pass" であればログインできるようにする。
  • ログインエラーであれば、error.html へ遷移する。
  • ログイン後の Hello 画面には、ログインIDを表示する。

では、順番にコーディングしていきます。

コーディング1

ログイン画面は、login.naでアクセス可能とする。 getAliasURI メソッドを以下のように変更します。

tutorial1/logic/LoginHtml.java②
1:
2:
3:
public String getAliasURI() {
  return "login.na";
}

これでブラウザから、http://localhost:8080/tutorial1/login.na でアクセスできるようになりました。

コーディング2

ログイン画面からログインする時、ユーザーIDとパスワードは必須入力項目とする。 ユーザーIDは何でも良い事とし、パスワード"pass"であればログインできるようにする。 ログインエラーであれば、error.htmlへ遷移する。

まず、自分自身のページに何が入力されたかを知る方法からです。
tutorial1.view.LoginHtml(今修正中の tutorial1.logic.LoginHtml の親クラス)には、以下のメソッドがあります。

1 public paraselene.tag.form.SingleTextBox getUserIdInput() nameが"user_id"であるテキストボックスを得る。
2 public paraselene.tag.Tag[] getUserIdTags()
3 public paraselene.tag.form.SingleTextBox getPasswordInput() nameが"password"であるテキストボックスを得る。
4 public paraselene.tag.Tag[] getPasswordTags()
これはツールにより生成されたものですが、このうち 1番(ユーザーID)と3番(パスワード)を使います。
2番と4番のメソッドについては、ここでは触れません。

スケルトン生成ツールは、以下の規則でメソッドを命名します。
get name設定値 タグの種類
1番であれば、UserId が "user_id" より、そして <INPUT> タグであるのでこの名称となっています。
このように、name が設定されたタグには全て、アクセッサが準備されます。
例えば、getUserIdInput のソースは、以下のように書かれています。

tutorial1/view/LoginHtml.java③
1:
2:
3:
public paraselene.tag.form.SingleTextBox getUserIdInput() {
  return (paraselene.tag.form.SingleTextBox)getTag( "user_id" );
}

getTag( name 設定値 )により、同じ事が出来る事が分かります。
ただし通常は、getTag を直接呼ばず、getUserIdInput を使って下さい。
もしモックアップ HTML で "user_id" を "user_name" に変更しツールを通すと、getUserIdInput が消滅し、代わりに getUserNameInput が作られます。
このままコンパイルすると、getUserIdInputを使っている箇所でコンパイルエラーが発生します。
もし getTag("user_id") としていたら、実行時に Exception が発生するまでデザインが変更された事に気付かないかもしれません。

コーディング3

それでは、実際に LoginHtml の inputMain を書いてみます。

tutorial1/logic/LoginHtml.java④
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
    public Forward inputMain( RequestParameter req, Forward fw ) throws PageException {
        // fwにはモックアップと同じ遷移先が設定されています。
        // 別の遷移先としたい場合は、新しくForwardインスタンスを生成して
        // リターンして下さい。
        try {
            // 未入力チェック。
            Control.checkNull( null, getUserIdInput(), getPasswordInput() );
            // パスワード検証。
            if ( !getPasswordInput().getValueString().equals( "pass" ) ) {
                throw new ControlException( null, (Control)null );
            }
        }
        catch( ControlException ce ) {
            try {
                // error.htmlへ遷移させる。
                return new Forward( new java.net.URI( "error.html" ), false );
            }
            catch( java.net.URISyntaxException uri_e ) {
                // 例外発生により続行不能。
                throw new PageException( uri_e );
            }
        }
        return fw;
    }

入力エラーはこのように、ControlExceptionをキャッチした箇所でハンドリングすればよいでしょう。

error.html へ遷移させるには、Foward を new してリターンする事で行います。
また、 URI クラスコンストラクタは URISyntaxException をスローします。もしこれが発生すれば処理を続行できないので、 PageException をスローして処理を中断します。

コーディング4

error.html はそのままでは "login.html" へリンクしているので、修正しておきます。

error.html
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
</head>
<body>
<p>ログインできません。</p>
<p><a href="login.na">戻る</a></p>
</body>
</html>

コーディング5

最後に、HelloHtml の outputMain を修正します。

tutorial1/logic/HelloHtml.java
1:
2:
3:
4:
5:
6:
7:
    public Page outputMain( Page from, RequestParameter req ) throws PageException {
        // 出力内容を設定します。
        // 初期化したい場合は、init()をコールして下さい。
        LoginHtml   login = (LoginHtml)from;
        this.getUserIdSpan().setValueString( login.getUserIdInput().getValueString() );
        return this;
    }

from のインスタンスは、ログイン処理を行った LoginHtml インスタンスです。
そこからユーザーIDを取得します。
この時、ログイン画面のユーザーIDにと入力すると、Hello 画面には

こんにちは>テスト"さん。

と表示されます。

getValueString は、コントロール系のタグであれば value 属性を、そうでなければタグで囲まれた文字列を取得します。
setValueString も同様に上記の位置に文字列を設定します。
また上の入力例のように、Paraselene がエスケープ処理を行いますので、プログラム側は">テスト\""という文字列として扱えます。

例えばから getValueString すると、改行は"\n"に見えます。
それを<SPAN>へ setValueString を行うと、
1行目<BR>
2行目
と設定されます。

チュートリアル3(DummyPage)

最終更新: 2009/10/31
特に明示されていない限り、本Wikiの内容は次のライセンスに従います:CC Attribution 3.0 Unported
文書の先頭へ
SourceForge.JP
2009 © Akira Terasaki.  
Driven by DokuWiki The Apache Software Foundation