単純なプログラムを書換えよう(WebWork2編)−インタセプタでログ出力

はじめに

インタセプタを定義することで,アクションの前後に特定の処理を割込ませることができる.とりあえず,アクションの後にログを出してみよう.

ディレクトリ構成

observer-command-singletonのソースを元に,インタセプタの追加を行う.以下のファイルをコピーして,ファイル名やパッケージ名をcounter03からcounter06に修正する.

counter06-1.jsp/counter06-2.jsp
ブラウザに表示するためのjsp.counter06-1.jspは最初に表示する画面,counter06-2.jspはプログラム起動後に遷移する画面.
Command.class/DecCommand.class/IncCommamd.class/CommandDispatcher.class/Controller.class/Model.class
javaのクラス
xwork.xml
WebWork2の画面遷移等を指定するファイル

ディレクトリ構成は以下のようになる.各ファイルの内容はobserver-command-singletonのファイル名・パッケージ名を変更しただけである.

${TOMCAT_HOME}/webapps/counter/counter06-1.jsp
                              /counter06-2.jsp
                              /WEB-INF/classes/counter06/Command.class
                                                        /DecCommand.class
                                                        /IncCommand.class
                                                        /CommandDispatcher.class
                                                        /Controller.class
                                                        /Model.class
                                              /xwork.xml
                                      /lib/commons-logging.jar
                                          /ognl-2.6.3-modified.jar
                                          /oscore-2.2.1.jar
                                          /velocity-dep-1.3.1.jar
                                          /webwork-2.0.jar
                                          /xwork-1.0.jar
                                      /web.xml
    

URL

アクセスするためのURLは以下の通りとなる.

http://localhost:[ポート番号]/counter/counter06-1.jsp
    

ポート番号が8080の場合は,

http://localhost:8080/counter/counter06-1.jsp
    

となる.

counter06-1.jsp/counter06-2.jsp

counter03-1.jsp/counter03-2.jspを一部修正する.

<%-- counter06-1.jsp --%>
<html>
  <head>
    <title>counter06-1</title>
  </head>
  <body>
    value:0<br/>
    <form action="Counter06.action"> <%-- xwork.xmlの記述にあわせてCounter03をCounter06に変更 --%>
      <input type="hidden" name="value" value="0""/>
      <input type="submit" name="action" value="inc"/>
      <input type="submit" name="action" value="dec"/>
    </form>
  </body>
</html>
<%-- end --%>

<%-- counter06-2.jsp --%>
<%@ taglib prefix="ww" uri="webwork" %>
<html>
  <head>
    <title>counter06-2</title>
  </head>
  <body>
    value:<ww:property value="value"/><br/>
    <form action="Counter06.action"> <%-- xwork.xmlの記述にあわせてCounter03をCounter06に変更 --%>
      <input type="hidden" name="value" value="<ww:property value="value"/>"/>
      <input type="submit" name="action" value="inc"/>
      <input type="submit" name="action" value="dec"/>
    </form>
  </body>
</html>
<%-- end --%>
    

Command.java/IncCommand.java/DecCommand.java

パッケージ名をcounter03からcounter06へ変えただけ.

//Command.java
package counter06; <!-- counter03をcounter06に変更 -->

public interface Command{
  void execute(); 
}
//end

//IncCommand.java
package counter06; <!-- counter03をcounter06に変更 -->

public class IncCommand implements Command{
  private Model model;

  public IncCommand(Model model){ 
    this.model=model;
  }
  public void execute(){
    model.inc();
  }
}
//end

//DecCommand.java
package counter06; <!-- counter03をcounter06に変更 -->

public class DecCommand implements Command{
  private Model model;

  public DecCommand(Model model){ 
    this.model=model;
  }
  public void execute(){
    model.dec();
  }
}
//end
    

CommandDispatcher.java

パッケージ名をcounter03からcounter06へ変えただけ.

//CommandDispatcher.java
package counter06; <!-- counter03をcounter06に変更 -->

import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.*; 

public class CommandDispatcher{
  private static Map commands=new HashMap();
  private static CommandDispatcher commandDispatcher=new CommandDispatcher();
  private static Model model;

  static{
    commands.put("inc" ,"counter03.IncCommand");
    commands.put("dec" ,"counter03.DecCommand");
  }
  private CommandDispatcher(){
  }
  public static CommandDispatcher getInstance(Model model){ 
    commandDispatcher.model=model;
    return commandDispatcher;
  }

  public Command getCommand(String command){
    Command executeCommand=null;
    try{
      executeCommand=
        (Command)Class.forName(commands.get(command).toString())
          .getConstructor(new Class[]{this.model.getClass()})
            .newInstance(new Object[]{this.model}); 
    }
    catch(ClassNotFoundException e){
      e.printStackTrace();
    }
    catch(NoSuchMethodException e){
      e.printStackTrace();
    }
    catch(InstantiationException e){
      e.printStackTrace();
    }
    catch(IllegalAccessException e){
      e.printStackTrace();
    }
    catch(InvocationTargetException e){
      e.printStackTrace();
    }
    return executeCommand;
  }
}
//end
    

Controller.java

パッケージ名をcounter03からcounter06へ変えただけ.

//Controller.java
package counter06; <!-- counter03をcounter06に変更 -->

import com.opensymphony.xwork.*;

public class Controller extends ActionSupport{
  private Model model=new Model();
  private CommandDispatcher commandDispatcher=CommandDispatcher.getInstance(model);
  private String action;

  public void setValue(String value){
    model.setValue(Integer.parseInt(value));
  }
  public String getValue(){
    return Integer.toString(model.getValue());
  }
  public void setAction(String action){
    this.action=action;
  }
  public String getAction(){
    return action;
  }
  public String execute(){
    commandDispatcher.getCommand(this.getAction()).execute();
    return SUCCESS;
  }
}
//end
    

Model.java

パッケージ名をcounter03からcounter06へ変えただけ.

//Model.java
package counter06; <!-- counter03をcounter06に変更 -->

public class Model{
  private int value;
  
  public Model(){
    this(0);
  }
  public Model(int value){
    setValue(value);
  }

  public int getValue(){
    return value;
  }
  public void setValue(int value){
    this.value=value;
  }

  public int inc(){
    return inc(1);
  }
  public int inc(int value){
    setValue(getValue()+value);
    return getValue();
  }
  public int dec(){
    return dec(1);
  }
  public int dec(int value){
    setValue(getValue()-value);
    return getValue();
  }
}
//end
    

デフォルトのログを出力する

まず,xworkがデフォルトで持っている形式でログを出力してみる.そのためには,xwork.xmlに以下の修正を加える.

<!-- xwork.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xwork
  PUBLIC
  "-//OpenSymphony Group//XWork 1.0//EN"
  "http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
  <include file="webwork-default.xml"/>

  <package name="default" extends="webwork-default">
    <interceptors>
      <!-- ログ出力用のインタセプタを定義 -->
      <interceptor-stack name="defaultLoggingStack">
        <interceptor-ref name="logger"/>
        <interceptor-ref name="defaultStack"/>
      </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="defaultStack"/>

    <!-- Counter06を追加 -->
    <action name="Counter06" class="counter06.Controller">
      <result name="success" type="dispatcher">
        <param name="location">counter05-2.jsp</param>
      </result>
      <interceptor-ref name="defaultLoggingStack"/> <!-- 上で定義したログ出力用のインタセプタ -->
    </action>
  </package>
</xwork>
<!-- end -->

    

URLにアクセスしてカウンタを動かしてみよう.その後,${TOMCAT_HOME}/logs/stdout.logを見ると,次のような内容のログがあるはずである.

2004/07/16 0:54:48 com.opensymphony.xwork.interceptor.LoggingInterceptor logMessage
情報: Starting execution stack for action Counter06
2004/07/16 0:54:48 com.opensymphony.xwork.interceptor.LoggingInterceptor logMessage
情報: Finishing execution stack for action Counter06
        

ログ出力の仕組み

まず,ログ出力を行うためには,xwork.xmlに<interceptor-ref name="logger"/>を追加する必要がある.この記述にひもづいているプログラムはwebwork-default.xmlを見ると分かる.このファイルは${TOMCAT_HOME}/webapps/counter/WEB-INF/lib/webwork-2.0.jarの中にある.こんな内容が書いてある.

<interceptor name="logger" class="com.opensymphony.xwork.interceptor.LoggingInterceptor"/>
        

com.opensymphony.xwork.interceptor.LoggingInterceptorの中身はxworkのソースコードをダウンロードすると確認できる.上述したようなメッセージをActionクラスの起動前後に出力している.

独自ログの出力

では,プログラムを作って独自のログを出力しよう.そのためにはxwork.xmlにインタセプタを定義する必要がある.クラス名はcounter06.Loggerとしよう.ディレクトリ構成は以下のようになる.

${TOMCAT_HOME}/webapps/counter/counter06-1.jsp
                              /counter06-2.jsp
                              /WEB-INF/classes/counter06/Command.class
                                                        /DecCommand.class
                                                        /IncCommand.class
                                                        /CommandDispatcher.class
                                                        /Controller.class
                                                        /Model.class
                                                      /Logger.class
                                              /xwork.xml
                                      /lib/commons-logging.jar
                                          /ognl-2.6.3-modified.jar
                                          /oscore-2.2.1.jar
                                          /velocity-dep-1.3.1.jar
                                          /webwork-2.0.jar
                                          /xwork-1.0.jar
                                      /web.xml
    
<!-- xwork.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xwork
  PUBLIC
  "-//OpenSymphony Group//XWork 1.0//EN"
  "http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
  <include file="webwork-default.xml"/>

  <package name="default" extends="webwork-default">
    <interceptors>
      <interceptor name="logger" class="counter06.Logger"/> <!-- 独自ログ出力用のクラスをloggerに定義 -->

      <interceptor-stack name="defaultLoggingStack">
        <interceptor-ref name="logger"/>
        <interceptor-ref name="defaultStack"/>
      </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="defaultStack"/>

    <action name="Counter06" class="counter06.Controller">
      <result name="success" type="dispatcher">
        <param name="location">counter05-2.jsp</param>
      </result>
      <interceptor-ref name="defaultLoggingStack"/>
    </action>
  </package>
</xwork>
<!-- end -->

        

counter06.Loggerのソースは以下の通り.

package counter06;

import java.util.logging.*;
import com.opensymphony.xwork.interceptor.AroundInterceptor;
import com.opensymphony.xwork.ActionInvocation;

public class Logger extends AroundInterceptor{
  private static final java.util.logging.Logger logger
    =java.util.logging.Logger.getLogger("counter06.Logger");
  //クラス名Loggerがパッケージcounter06とjava.util.logging.loggerで重複した.
  //名前変えればパッケージ名から指定する必要はないんだけど

  protected void before(ActionInvocation invocation) throws Exception{
    //AroundInterceptorはbefore/afterメソッドを定義しないといけない.beforeは何もしないので空で作る
  }

  protected void after(ActionInvocation invocation,String result) throws Exception{
    Controller controller=(Controller)invocation.getAction();
    logger.info("result:"+result+" value:"+controller.getValue());
  }
}
        

ログを出力するためには,抽象クラスAroundInterceptorを継承し,before/after二つのメソッドを持つ必要がある.before/afterメソッドの引数として受取るActionInvocationのgetActionメソッドで,アクションの主体となるクラス(Actionインタフェースの実装クラス)を取得できる.今回の例の場合はControllerクラスとなる.ControllerクラスのgetValueメソッドを使って,更新後の値を得ている.

文字列型のresultはActionクラスのexecuteメソッドが返す文字列である.

その結果,${TOMCAT_HOME}/logs/stdout.logに以下のようなログを出力する.

2004/07/24 23:23:10 counter07.Logger after
情報: result:success value:1
2004/07/24 23:23:11 counter07.Logger after
情報: result:success value:2