連載
» 2004年09月28日 10時00分 公開

JavaTips 〜Apache/Jakarta編:Strutsのアクションマッピングに独自パラメータを追加

[佐藤匡剛,@IT]

 フレームワークとは、それが対象とする問題領域への、一般的な解法となるアプリケーション構築の基盤を提供するものです。そのため、多くのユーザーにとっては、フレームワークが提供する機能だけでアプリケーションの構築が可能ですが、その一方で、より高度な使い方をしたいときに、デフォルトで提供されている機能だけでは、かゆいところに手が届かない、といったことも当然あります。

 そこで、一般にフレームワークでは、デフォルトの機能だけでは不十分なときに、ユーザーが独自に機能を付け足すことができるような、フレームワーク拡張の仕組みが提供されているかどうかが重要になってきます。

 Strutsでは、このようなフレームワーク拡張のための仕組みが、豊富に用意されています。本稿では、そのうちActionMappingクラスを独自に拡張する方法について紹介します。ActionMappingクラスとは、Struts構成ファイル(struts-config.xml)にて定義された、URLとActionクラスとのマッピング情報を保持するクラスになります。

注:Strutsのインストールと動作設定については、「Strutsを使うWebアプリケーション構築術」(Java Solution)もしくは「サーバサイド技術の学び舎 - WINGS」にある「サーバサイド環境構築設定」を参照してください。

ActionMappingクラスの拡張とその設定方法

 ここでは、説明のために、独自のロギング機能を持った拡張クラスを登録してみます。ActionMappingを拡張するには、org.apache.struts.action.ActionMappingのサブクラスを実装します。

 以下に示すLoggingActionMappingクラスが、ActionMappingの拡張になります。このクラスは、新しい機能として、特定のログファイル(ここでは「C:\struts.log」)へログを書き出すメソッドwriteLogを持ち、Actionクラスからそのメソッドが呼ばれることで、ログへの出力を行う機能を持ちます。また、パラメータfLogLevelによって、出力するログのレベルを設定できるようになっています。なお、このパラメータは、Struts構成ファイルstruts-config.xmlにて初期値を設定できます。

LoggingActionMapping.java
package net.mogra.wings.javatips;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionMapping;

public class LoggingActionMapping extends ActionMapping {

  private static final String LOG_FILE = "C:\\struts.log";
  public static final int ERROR = 1;
  public static final int WARN = 2;
  public static final int INFO = 4;
  private int fLogLevel = 0;
  public LoggingActionMapping() { super(); }

  // 以下のセッターメソッドを宣言することで、struts-config.xmlでの
  // パラメータ設定が可能になる

  public void setLogLevel(int logLevel) { fLogLevel = logLevel; }
  public int getLogLevel() { return fLogLevel; }
  public void writeLog(HttpServletRequest request
                    , String message, int logLevel) {
    // 渡されたログレベルが、設定値より高い重要度であれば出力する
    if (logLevel <= fLogLevel) {

      BufferedWriter writer = null;
      try {
        Calendar now = Calendar.getInstance();
        writer = new BufferedWriter(new FileWriter(LOG_FILE, true));
        writer.write("[" + now.getTime().toString() + "]");
        writer.write(" ");
        writer.write(request.getRequestURI());
        writer.write(" ");
        writer.write(message);
        writer.newLine();
        writer.close();
      } catch (Exception e) {
        if (writer != null) try {
          writer.close();
        } catch (IOException e2) {
          e2.printStackTrace();
        }
      }
    }
  }
}


 struts-config.xmlでは、以下のように、<action-mappings>要素のtype属性にActionMappingサブクラスを指定することで、登録を行います(赤字部分)。ActionMappingサブクラスに独自のプロパティを渡すには、各<action>要素内に<set-property>子要素を記述し、そのproperty属性にプロパティ名、value属性にその値を指定します。このプロパティ名をXXXとした場合、その値がActionMappingサブクラスのセッターメソッドsetXXXに渡されることになります。

struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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>
  ...中略...
  <action-mappings 
    type="net.mogra.wings.javatips.LoggingActionMapping">
    <action path="/hello"
      type="net.mogra.wings.javatips.HelloAction">
      <set-property property="logLevel" value="4"/>
      <forward path="/hello.jsp" name="success"/>
    </action>
    ...中略...
  </action-mappings>
  ...中略...
</struts-config>


 上記の方法だと、登録されたすべての<action>要素に対して、そのActionMappingサブクラスが用いられることになります。特定の<action>要素に対してだけ、ActionMappingサブクラスを利用したいときは、次のように書くこともできます(赤字部分)。この場合、親要素<action-mappings>にtype属性が定義されていなければ、その他の<action>要素にはデフォルトのActionMappingクラスが適用されます。

struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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>
  ...中略...
  <action-mappings>
    <action path="/hello"
      className="net.mogra.wings.javatips.LoggingActionMapping"
      type="net.mogra.wings.javatips.HelloAction">

      <set-property property="logLevel" value="4"/>
      <forward path="/hello.jsp" name="success"/>
    </action>
    ...中略...
  </action-mappings>
  ...中略...
</struts-config>


注:Struts1.0では、ActionMappingサブクラスの登録はstruts-config.xmlで行わず、web.xmlで配備されるActionServletのmappingパラメータより行いました。上記のような方法に変更されたのは、Struts1.1からです。なお、下位互換性を保つために、Struts1.1でも、web.xmlでActionMappingサブクラスを登録する方法も残されています。

 これで、ActionMappingクラスの拡張と登録は終了です。

 以下は、このサンプルで用いるActionクラス(HelloAction.java)と、そこからフォワードされる表示(hello.jsp)になります。HelloActionクラス中で、ActionMappingの独自拡張部分を利用するには、executeメソッドで渡される引数mappingを、LoggingActionMappingクラスへ明示的に型キャストしてやらなくてはならないことに注意してください。executeメソッドへは、実際にはサブクラスが渡される場合でも、引数のクラスはActionMappingクラスになっているためです。

HelloAction.java
package net.mogra.wings.javatips;

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 HelloAction extends Action {
  public ActionForward execute(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response) {
    // ロギング部分
    // LoggingActionMappingへ明示的に型キャストする必要がある

    ((LoggingActionMapping) mapping).writeLog(request, "にリクエストがありました。", LoggingActionMapping.INFO);
    return mapping.findForward("success");
  }
}


Hello.jsp
<%@ page contentType="text/html;charset=Windows-31J" language="java" %>
<html>
<body>
こんにちは
</body>
</html>
+++/スクリプト+++


 それでは、LoggingActionMappingを登録したパス「/hello.do」へアクセスしてみましょう。ブラウザには、以下のように表示されるはずです。

「/hello.do」の表示 「/hello.do」の表示

 そして、ログ「C:\struts.log」が以下のように出力されていれば成功です。下のサンプルは、「/hello.do」に3回アクセスした結果です。

実行結果
[Fri Aug 13 10:50:41 JST 2004] /javatips/hello.do にリクエストがありました。
[Fri Aug 13 10:50:41 JST 2004] /javatips/hello.do にリクエストがありました。
[Fri Aug 13 10:50:42 JST 2004] /javatips/hello.do にリクエストがありました。


 このように、Strutsでは、ActionMappingに独自の機能を追加することができるわけです。

 なお、このTIPSではActionMappingの拡張を説明するために、Actionのロギング機能をActionMapping部分で実装しました。しかし、実際のStrutsアプリケーションを開発する場合には、Actionの各サブクラスにおいて、JakartaのCommons Loggingフレームワークを用いてロギングを実装するのが一般的です。Commons Loggingフレームワークは、Strutsにすでに組み込まれている(Strutsインストール時に、Commons Loggingのjarファイルもクラスパスに含める必要がある)ため、特別な設定の必要なしに利用できます。

Profile

WINGSプロジェクト

佐藤匡剛MOGRA DESIGN, Ltd.


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。