ServletConfigInterceptorはActionクラスにHttpServletRequest、HttpServletResponse、ServletContextなどの情報を設定してくれる。ただし、設定される為にはサポートされてるインターフェースをActionで継承しなければならない。サポートされるのは以下のインターフェース。

  • ServletContextAware
  • ServletRequestAware
  • ServletResponseAware
  • ParameterAware
  • RequestAware -
  • SessionAware
  • ApplicationAware
  • PrincipalAware


仮にサポートされるすべてのインターフェースを継承したクラスを作成すると以下のようになる。各setterメソッドに対してServletConfigInterceptorが値を設定してくれる。


import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.PrincipalAware;
import org.apache.struts2.interceptor.PrincipalProxy;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.util.ServletContextAware;


public class HogeAction extends ActionSupport implements ServletContextAware,
ServletRequestAware,
ServletResponseAware,
ParameterAware,
RequestAware,
SessionAware,
ApplicationAware,
PrincipalAware{

private ServletContext servletContext;
private HttpServletRequest request;
private HttpServletResponse response;
private Map parameterMap;
private Map requestMap;
private Map sessionMap;
private Map applicationMap;
private PrincipalProxy principalProxy;

public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public void setParameters(Map parameterMap) {
this.parameterMap = parameterMap;
}
public void setRequest(Map requestMap) {
this.requestMap = requestMap;
}
public void setSession(Map sessionMap) {
this.sessionMap = sessionMap;
}
public void setApplication(Map applicationMap) {
this.applicationMap = applicationMap;
}
public void setPrincipalProxy(PrincipalProxy principalProxy) {
this.principalProxy = principalProxy;
}
}

ServletContextAware、ServletRequestAware、ServletResponseAwareを継承してしまうとservletApiに依存したクラスになってしまうので注意が必要。(テストがしにくくなるとか、フレームワークがせっかく隠してくれてたのに〜 っているのが理由)リクエストの値がとりたい場合はRequestAware、セッションの値がとりたい場合はSessionAwareを継承しとけば値がとれる(かつservletApiにも依存しなくて済む)のでそれらの値がとりたいだけであれば、上記インターフェースの継承は不要。

また、SessionAware,RequestAware、ParameterAware、を継承してServletConfigInterceptorに値を設定してもらわなくても、Action内で直接以下のように書けば同じように値を取得できる(ServletConfigInterceptorがやっていることと一緒だけど。。)
ActionContext.getContext().getSession().get("foo");
ActionContext.getCongext().get("request").get("bar");
ActionContext.getParameters().get("hoge");


Actionに対して使用するインターセプターをstruts-default.xmlで宣言しているinterceptor-stackを使用する場合はstruts.xmlに対して何も記述の必要はない。(struts-default.xmlで宣言されているすべてのinterceptor-stackにServletConfigInterceptorが入っているため)もし、自分でinterceptor-stackをstruts.xmlに宣言して、その中にServletConfigInterceptorを追加したい場合は以下の記述を追加する。
<interceptor-ref name="servletConfig"/>

2009/07/29

Hello, Scala!


Scalaが最近,将来のJava 後継者の有力候補として注目を集めている。Java の創作者である James Gosling 氏やJRuby の主要開発者である Charles Nutter氏に続いて,Groovy の創作者であるJames Strachan氏もScalaへの賛意を表明している。

Javaの将来的な後継者としての Scala


この記事でScalaに興味が湧き、いろいろといじり始めている。英語ダメダメなのに洋書のProgramming in Scalaも買ってしまった。

まずはお決まりのやつ

object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello, Scala!")
}
}

ExceptionMappingInterceptorは例外処理のハンドリングをやってくれる。
具体的には、例外を補足して遷移先画面の振り分けを行う。


<package name="example" extends="struts-default">
<global-results>
<result name="error">error.jsp</result>
</global-results>

<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error"/>
</global-exception-mappings>

<action name="Foo" class="example.FooAction" >
<interceptor-ref name="defaultStack"/>
<exception-mapping exception="example.HogeException" result="hoge_error"/>
<result >/example/Foo.jsp</result>
<result name="hoge_error">/example/hoge_error.jsp</result>
</action>
</package>

上記struts.xmlだと、exampleという名前でグループ化したURLでjava.lang.Exceptionが発生した場合、error.jspに飛ばされる。また、example.FooAction呼び出しの際にexample.HogeExceptionが発生した場合、example/hoge_error.jspに飛ばされる。

ExceptionMappingInterceptorを使用する際に気をつけることは、複数のインターセプターを呼ぶ場合は1番最初に呼び出すようにする。つまり<interceptor-ref name="exception"/>を1番最初に書くようにする。
理由は、ExceptionMappingInterceptorよりも前に別のインターセプターを呼び出し、そのインターセプターで例外が発生しても補足ができないため。

上記struts.xmlでは<interceptor-ref name="exception"/>はどこにも書いてない。理由はstruts-defaultパッケージを継承しており、そこに含まれているdefaultStackを使用しているため。defaultStackには<interceptor-ref name="exception"/>が含まれている。
フレームワークが用意しているinterceptor-stackはすべて<interceptor-ref name="exception"/>が含まれており、大抵のものが先頭に書いてある。

もしフレームワークが用意しているinterceptor-stackを使用せず、インターセプターの定義をすべて自分で書くのであれば<interceptor-ref name="exception"/>を書くことと、書く順番に注意する。

ExceptionMappingInterceptorで補足した例外の内容(Throwable.getMessage)をログに出力したい場合は以下のような書き方をする。

<interceptor-ref name="exception">
<param name="logEnabled">true</param>
<param name="logCategory">jp.co.hogehoge</param>
<param name="logLevel">WARN</param>
</interceptor-ref>

<param name="logEnabled">true</param>は必須で <param name="logLevel">は記載がないとdebugレベルになる。(trace, debug, info, warn, error, fatal以外の値を書くとIllegalArgumentExceptionが発生)

ExceptionMappingInterceptorの処理はexception-mappingのresult要素に関連付けられた画面に飛ばすだけだが、他に何か処理をしたい場合は
publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder)
という拡張ポイントが用意されているのでExceptionMappingInterceptorを継承したクラスを作成し、上記メソッドに処理を書くとよいかもしれない。

2009/07/25

netbeansよさげ

最近Scalaを覚え始め、eclipseにscalaのプラグインを入れてコーディングし始めたけど、プラグインが全然使い物にならない。(mainメソッドがあるのにmainメソッドがないので実行できないと怒られたり、undoが効かなくなったりする。)

で、netbeansにもscalaプラグインがあるみたいなのでnetbeansでコーディングしてみると全然問題なし。scalaプラグインの件を別にしてもnetbeansは昔に比べるとすごく使いやすくなってる気がする。なので、これを機にメインをIDEをnetbeansに変えちゃおうと思う。

AliasInterceptorはパラメータにエイリアスをつける。


<package name="example" namespace="/hogehoge" extends="struts-default">
<action name="hoge" class="example.HogeAction" method="browse">
<param name="aliases">#{ 'foo' : 'bar' }</param> - (1)
<interceptor-ref name="alias"/> - (2)
<result name="success">/date.jsp</result>
</action>
</package>

class HogeAction{
private String foo;
private String bar;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}

上記のようなstruts.xmlとActionクラスがあるとする。
通常、http://struts2/hogehoge/hoge.action?foo=aaaとリクエストが来たらHogeActionクラスのfooに対して"aaa"が設定されるが、(1)と(2)があることによりHogeActionクラスのbarに対して"aaa"が設定される。複数のパラメータに対してエイリアスをつけたい場合は#{ 'foo' : 'bar', 'foo2' : 'bar2'}のようにカンマで区切れば良い。また、デフォルトのエイリアスキー"aliases"を変更したい場合は、以下のようにすればよい。(以下は"booboo"に変更)


<package name="example" namespace="/hogehoge" extends="struts-default">
<action name="hoge" class="example.HogeAction" method="browse">
<param name="booboo">#{ 'foo' : 'bar' }</param>
<interceptor-ref name="alias">
<param name="aliasesKey">booboo</param>
</interceptor-ref>
<result name="success">/date.jsp</result>
</action>
</package>

InterceptorはActionの実行前や実行後に処理を差し込むことができる。
Actionに対してinterceptorを追加、変更、削除したい場合はstruts.xmlを修正するだけで済む。
com.opensymphony.xwork2.interceptor.Interceptorやcom.opensymphony.xwork2.interceptor.AbstractInterceptorを継承して自分でInterceptorを作ることもできるが、大抵のものはFrameworkで用意されているのでそれを使用するとよい。(struts-default.xmlのinterceptor要素でFrameworkで用意されているinterceptorが定義されている。)

サンプル1


<package name="example" namespace="/hogehoge" extends="struts-default">

<action name="Foo" class="example.Foo" method="insert">
   <interceptor-ref name="defaultStack"/>
<result>/example/Foo.jsp</result>
</action>

<action name="Bar" class="example.Bar" method="update">
   <interceptor-ref name="defaultStack"/>
<result>/example/Foo.jsp</result>
</action>
<package/>

example.Foo.insert(),example.Bar.update()が実行される前にdefaultStackというinterceptorが実行される。interceptor-refのname属性は1つのinterceptorにつけられた名前、または複数のinterceptorをひとまとめにしたものに名前をつけたもになている。struts-default.xmlで確認するとわかるが、上記のdefaultStackというinterceptorは複数のinterceptorを1まとめにして名前をつけたものになっている。
また、上記のようにpackage要素内それぞれのactionで同じinterceptorを使うのであれば下記のようにdefault-interceptor-refを使った書き方もできる。


サンプル2

<package name="example" namespace="/hogehoge" extends="struts-default">
<default-interceptor-ref name="defaultStack"/>

<action name="Foo" class="example.Foo" method="insert">
<result>/example/Foo.jsp</result>
</action>

<action name="Bar" class="example.Bar" method="update">
<result>/example/Foo.jsp</result>
</action>
<package/>



自分で作成したinterceptorやstruts-default.xmlに記載してある方法以外でひとまとめにしたものを使用したい場合は下記のように定義をすればよい

サンプル3

<package name="example" namespace="/hogehoge" extends="struts-default">
<interceptors> - (1)
<interceptor name="hogehoge" class="example.HogeHogeInterceptor"> - (2)

<interceptor-stack name="defaultStackHogeHoge"> - (3)
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="hogehoge"/>
</interceptor-stack>
<interceptors>

<action name="Foo" class="example.Foo" method="insert">
<interceptor-ref name="hogeHoge"/>
<result>/example/Foo.jsp</result>
</action>

<action name="Bar" class="example.Bar" method="update">
<interceptor-ref name="defaultStackHogeHoge"/>
<result>/example/Foo.jsp</result>
</action>
<package/>

定義の仕方は(1)のようにinterceptors要素を定義し、子要素として、新たなinterceptorの定義は(2)のように、新たなinterceptorをまとめたものの定義は(3)のように記載する。

result要素の属性には、以下の値が設定できる
name: マッピング名。省略した場合は"success"になる
type: Actionクラスの処理終了後に呼ばれるタイプ。省略した場合は"dispatcher"になる

tpyeに設定できる値としてデフォルトでは以下のものが設定できる

Type説明
chain他のActionクラスを呼び出す
dispatcherviewにjspを使う
freemarkerviewにfreemarkerを使う
httpheaderHTMLのヘッダーのみ返す
redirect他のURLへリダイレクトする
redirectAction他のActionクラスへリダイレクトする
streamファイルのダウンロードで使用
velocityviewにvelocityを使う
xsltviewにxsltを使う
plainTextviewにplainTextを使う


上記の値はstruts-default.xml内のresult-typeに定義されている。

サンプルを使って説明

<package name="example" namespace="/hogehoge" extends="struts-default">
<action name="Foo" class="example.Foo">
<result>/example/Foo1.jsp</result>
<result name="input">/example/Foo2.jsp</result>
</action>
<package/>
  • example.Fooクラスが"success"を返すと/example/Foo1.jspが呼ばれる。
  • example.Fooクラスが"input"を返すと/example/Foo2.jspが呼ばれる。


struts-default.xml内に定義してあるもので要件を満たせなければ、com.opensymphony.xwork2.Resultインターフェース、もしくはそれらの子クラスを継承する形で自分で作れば良い。作ったものはstruts-default.xmlで記載してあるように<result-types>タグでくくって宣言すれば使えるようになる


<package name="example" namespace="/hogehoge" extends="struts-default">
<result-types>
<result-type name="freemarker2" class="example.FreemarkerResult2" />
</result-types>

<action name="Foo" class="example.Foo">
<result type="freemarker2">/example/Foo.ftl</result>
</action>
<package/>