単純なプログラムを書換えよう(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