2003/10/23 作成
ここ何ヶ月かの在庫整理。
Actionを拡張する
通常アクションはActionクラスを直接継承して作成しますが、一枚システム共通のActionをはさむことで、共通処理や前後のフック処理などを記述することが出来ます。
Action ↑(継承) MyBaseAction ↑(継承) SomeAction
MyBaseActionの例
public class MyBaseAction extends Action {
public final String OK = "ok";
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
String result = execute(form, request);
if (result == null) {
return null;
} else {
return mapping.findForward(result);
}
}
protected String execute(ActionForm form, HttpServletRequest request) throws Exception {
return null;
}
}
この例では、共通の定数と、引数の少ないexecuteメソッドをサブクラスに提供しています。なお、共通の前後処理を記述するような場合は、(Actionクラスでも可能ですが)後で触れるRequestProcessorを利用します。
ActionFormを拡張する
ActionFormもActionと同じように、共通の基底クラスを作っておけば、後で融通が利くでしょう。
ActionForm ↑(継承) MyBaseActionForm ↑(継承) SomeActionForm
次の例では、カスタマイズしたActionErrorsクラスを使ってvalidateを実行しています。
public class MyBaseActionForm extends ActionForm{
public final ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
MyActionErrors errors = new MyActionErrors();
validate(errors, mapping, request);
return errors;
}
protected void validate(MyActionErrors errors, ActionMapping mapping, HttpServletRequest request){
}
}
Strutsでリクエストを処理するメインのクラスがorg.apache.struts.action.RequestProcessorです。RequestProcessorを継承し、継承したクラスをstruts-config.xmlに記述することで、自作のRequestProcessorが利用できます。
struts-config.xmlへ登録する例
<controller
processorClass="com.muimi.MyRequestProcesser"
/>
controllerでは、multipartClassも拡張可能
RequestProcessorを拡張する例
public class MyRequestProcesser extends RequestProcessor{
protected boolean processRoles(
HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping)
//認証のチェック
}
protected ActionForward processActionPerform(
HttpServletRequest request,
HttpServletResponse response,
Action action,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
try {
//共通Action前処理
ActionForward forward = action.execute(mapping, form, request, response);
//共通Action後処理
return forward;
} catch (Exception e) {
return (processException(request, response,
e, form, mapping));
}
}
}
RequestProcessorのprocessメソッドがメインとなるメソッドで、そこから呼ばれるメソッドをオーバーライドして、あれこれと処理を追加できます。共通の認証チェック、ロギング、セッショントラッキングなどの処理を追加するのに適しているでしょう。なお、このクラスは、(*.doなどで)strutsのActionServletが呼ばれないと呼ばれないので、直接JSPを読んでいるような場合には、注意が必要です(共通処理をFilterに書くか、などの検討)。
Strutsカスタムタグを拡張する
カスタムタグもモノによって(拡張が考慮されたタグなら)比較的簡単に拡張できます。次の例では、bean:write(nested:write)タグのフォーマット機能を拡張した例です。
bean:write(nested:write)を拡張した例
public class MyWriteTag extends NestedWriteTag{
private String kind;
public void setKind(String kind){
this.kind = kind;
}
protected String formatValue(Object valueToFormat)
throws JspException {
if(valueToFormat == null){
return "";
}
if(kind == null){
return valueToFormat.toString();
}else if(kind == "money"){
DecimalFormat fmt = new DecimalFormat("###,###,###.###");
return fmt.format(valueToFormat);
}else{
return super.formatValue(valueToFormat);
}
}
}
bean:writeタグはformat属性である程度のフォーマットに対応していますが、システム独自の形にフォーマットしたい場合があります。この例では、kindで指定した値で、フォーマットします。
tldには次のように普通に登録します。継承元の他の属性値(formatなど)も使いたい場合は、その属性も記述します。
<tag>
<name>mywrite</name>
<tag-class>com.muimi.MyWriteTag</tag-class>
<body-content>EMPTY</body-content>
<attribute>
<name>name</name>
<required>false</required>
</attribute>
<attribute>
<name>property</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>kind</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
日本語(マルチバイト文字)に関する問題がたまにあります。Struts1.1時点では、私が直面したところでは、次のような問題がありました。
FileUploadで、ファイル名が文字化けする。これはJSPの文字コードとサーバーのデフォルト文字コードが異なる場合に発生します。これは、デフォルトで使われるCommonsMultipartRequestHandlerが文字コードを特に設定しないためです。解決策としてはcontrollerのmultipartClassを差し替える方法があります。
http://www.freeml.com/message/struts-user@freeml.com/0002603
linkタグのパラメータ(paramNameやpropertyなど)は、自動的にURLEncodeされますが、JSPがUTF-8以外の場合、日本語が文字化けします。これは、StrutsのRequestUtil#encodeURLでUTF-8決めうちでエンコードを行っているからです。現状解決策はありませんが、対処としては、JSPをUTF-8にする、パラメータ付の場合linkタグを使わない、パラメータに日本語をつけない、FORMでPOSTする(JavaScriptで)などが考えられます。
struts-config.xmlの分割1(DTD)
多くの人が同時に編集して、競合しやすいstruts-config.xmlはなんとか分割したいものです。XMLのENTITY参照という方法を使って、XMLファイルを分割することが出来ます。これはstrutsの機能でなく、XMLの仕組みです。
<?xml version="1.0" encoding="Shift_JIS" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd" [
<!ENTITY subform1 SYSTEM "configs/func1-form.xml">
<!ENTITY subaction1 SYSTEM "configs/func1-action.xml">
]>
<struts-config>
<form-beans>
...
&subform1;
...
</form-beans>
<action-mappings>
...
&subaction1;
...
</action-mappings>
...
この方法では、単にファイルを分ければいいだけなので、やることは簡単です。次のサブアプリケーションとは異なり、名前空間が分かれるわけではないので、名前が衝突しないように名前の付け方に注意する必要があります。
追記:単に大きすぎるファイルを分けるだけなら、エンティティ分割をしなくても、web.xmlにカンマ区切りで複数のstruts-config.xmlを登録すればいいだけのようです(ドキュメントの 5.3.2 Informing the Controller参照)。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml,/WEB-INF/struts-config2.xml</param-value>
</init-param>
...
struts-config.xml分割のもう1つの方法がサブアプリケーションを使う方法です。この方法では、サブアプリケーションごとに相対パスでactionやformが記述できます。注意点としては、サブアプリケーションを切り替える場合は、単にリンクを呼ぶだけではダメで、明示的にスイッチを行わなければなりません。
この場合のweb.xmlへの記述方法は次のようになります。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>config/sub</param-name>
<param-value>/WEB-INF/struts-config2.xml</param-value>
</init-param>
...
http://www.freeml.com/message/struts-user@freeml.com/0002040