便利なコードパーツ集!

やまろうのプログラミングTips

Java

WebWorkで掲示板開発!再利用しやすいActionを作る

投稿日:

2004/03

WebWorkについて簡単に説明すると、WebWorkは、JavaオープンソースプロジェクトOpenSymphonyによって開発されている
Webアプリケーションフレームワークです。

特徴は、
・シンプル
・Servlet APIを使わないActionを作れる
・柔軟な設定ファイル
・Velocityをサポート
といったところです。

仕事で使うってわけではないのですが、おもしろそうだったので使ってみました。
いろいろと特徴があるとはいえ、Webアプリケーションフレームワークなので
Strutsと似てます。なので簡単に使うことが出来ました。

使ってみての感想は、「設定ファイルにオプションがいっぱいあるなぁ」
いった感じです。とはいえ、ActionでServlet APIを使わないので
今まで覚えたServletテクニックが使えないというデメリットがあります。
「Servletでは、こうやったけど、WebWorkではどうやるんだ?」みたいなね。
StrutsはServletと同じでrequestとresponseを引数にもらうから
同じやり方で実装出来るんですけどねぇ。

ですが、ActionでServlet APIをつかわないことにはとてもメリットが
あります。それは、

「Actionのテストが簡単!!」

普通にJUnitでテスト出来ます。APサーバに配備しなくても動くんですな!

ということで今回はActionクラスを作ってJUnitでテストします。
ActionクラスはStrutsで掲示板で作ったののWebWork版を作ります。
パッケージ名だけ変えてクラス名はいっしょ。
それではソース行ってみよう!

package jp.gr.java_conf.yamarou.app.forum.webwork;

import com.opensymphony.xwork.ActionSupport;
import jp.gr.java_conf.yamarou.app.forum.*;

/**
 * 掲示板メッセージを読み込むAction。
 */
public class LoadMessageAction extends ActionSupport {
  private ForumMessageVO[] messages;

  public String execute() throws Exception {
    ForumDAO dao = new ForumDAO();
    try {
      this.messages = dao.getForumMessages();
    } catch (Exception ex) {
      super.addActionError(super.getText("error.system"));
    }
    return SUCCESS;
  }
  public ForumMessageVO[] getMessages() {
    return messages;
  }
}

package jp.gr.java_conf.yamarou.app.forum.webwork;

import com.opensymphony.xwork.ActionSupport;
import jp.gr.java_conf.yamarou.app.forum.*;

/**
 * 送信された名前、メッセージを保存するAction。
 */
public class SaveMessageAction extends ActionSupport {
  private String name;

  private String message;

  protected void doValidation() {
    if (name == null || name.equals("")) {
      super.addFieldError("name", getText("error.name.required"));
    }
    if (message == null || message.equals("")) {
      super.addFieldError("message", getText("error.message.required"));
    }
  }

  public String doExecute() throws Exception {
    ForumDAO dao = new ForumDAO();
    dao.save(this.name, this.message);
    return SUCCESS;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getMessage() {
    return message;
  }

  public String getName() {
    return name;
  }
}

package jp.gr.java_conf.yamarou.app.forum.webwork;

import jp.gr.java_conf.yamarou.app.forum.*;
import junit.framework.*;

public class ActionTest
    extends TestCase {

  public static void main(String args[]) {
    junit.textui.TestRunner.run(new TestSuite(ActionTest.class));
  }

  public ActionTest(String name) {
    super(name);
  }

  public void testAction() throws Exception {
    final String NAME = "yamarou";
    final String MSG = "hello";
    SaveMessageAction saveAction = new SaveMessageAction();
    saveAction.setName(NAME);
    saveAction.setMessage(MSG);
    saveAction.execute();
    
    LoadMessageAction loadAction = new LoadMessageAction();
    loadAction.execute();
    ForumMessageVO[] messages = loadAction.getMessages();
    assertEquals(NAME, messages[0].getName());
    assertEquals(MSG + System.getProperty("line.separator"),
                 messages[0].getMessage());
    
  }

}

Actionがやっている事はStruts編のActionと同じです。クラスの役割分担も
同じ。ただ、Validatorを使わなかった(WebWorkにもValidatorはある)ので、
入力チェックのコードが入っています。
SUCCESSって定数が親クラスで定義されている辺りが芸が細かいというか
痒い所に手が届くといった感じです。Strutsで"success"ってリテラルで書くのも
なんだし、かといって自分で定数作るのもばかばかしいなぁと思ってたので、
いいなーと思いました。他にもINPUTとか良く使いそうな定数が定義されています。

あと、入力エラーで
super.addFieldError("message", getText("error.message.required"));
のコードが実行されると、そのフィールドのすぐ上にエラーメッセージが
表示されるのが面白かったです。完成したらダウンロード出来るように
するのでぜひ実際に動かしてみてください。「オッ」っと思います。

それから、JUnitのTestCaseのコード(ActionTest)がありますが、普通に
コンソールから実行出来ます。簡単にテストが出来ました。(エラーメッセージの
テストは出来ないが)

ActionTestを実行したらバグが見つかりました。
ActionTestの
assertEquals(NAME, messages[0].getName());
のmessages[0].getName()の戻り値の末尾に改行が含まれているというバグ
でした。まぁ改行あっても見た目上変わらないですけどね、掲示板はHTMLだから。

なので、ForumDAOに41行目を
nameList.add(line.substring(9));
から
nameList.add(line.substring(9, line.length()));
に変更しました。

WebコンテナなしでActionをテスト出来るのは良い気がしますが、
テストするActionがLocalインターフェースのEJBにアクセスしていたら
結局、J2EEサーバに配備しなければ動かないわけなので、
LocalインターフェースのEJBを使用しない場合にはメリットがあるかなぁ
と感じました。

でもって、設定ファイル(xwork.xmlの一部抜粋)

<action name="sendMessage"
class="jp.gr.java_conf.yamarou.app.forum.webwork.SaveMessageAction">
<result name="input" type="dispatcher">
<param name="location">/forum.jsp</param>
</result>
<result name="success" type="chain">
<param name="actionName">loadMessage</param>
</result>
<result name="error" type="dispatcher">
<param name="location">/forum.jsp</param>
</result>
</action>
<action name="loadMessage"
class="jp.gr.java_conf.yamarou.app.forum.webwork.LoadMessageAction">
<result name="input" type="dispatcher">
<param name="location">/forum.jsp</param>
</result>
<result name="success" type="dispatcher">
<param name="location">/forum.jsp</param>
</result>
<result name="error" type="dispatcher">
<param name="location">/forum.jsp</param>
</result>
</action>

ここで
<result name="success" type="chain">
ってのがありますが、resultのtypeをdispatcherとすると
forwardしてchainにすると次のActionを実行するようです。
dispatcherにActionのパスを書いても次のActionを実行するわけですが(Strutsは
これしかない)そうするとforwardのオーバーヘッドが多少発生します。
chainと書くとforwardのオーバーヘッドがないのだと思います。
まさに僕がVol16でかいた「Chain of Action」パターンに最適です。

WebWorkってそういう小さな工夫がいっぱい詰まってて、それらを設定ファイル
で柔軟に制御することが出来るんだなぁと感じました。

スポンサーリンク

-Java

Copyright© やまろうのプログラミングTips , 2022 AllRights Reserved Powered by AFFINGER4.